8

背景

我们知道这个概念std::same_as与顺序无关(换句话说,对称):std::same_as<T, U>等同于std::same_as<U, T>相关问题)。在这个问题中,我想实现一些更通用的东西:template <typename ... Types> concept same_are = ...检查包中的类型是否Types彼此相等。

我的尝试

#include <type_traits>
#include <iostream>
#include <concepts>

template <typename T, typename... Others>
concept same_with_others = (... && std::same_as<T, Others>);

template <typename... Types>
concept are_same = (... && same_with_others<Types, Types...>);

template< class T, class U> requires are_same<T, U>
void foo(T a, U b) {
    std::cout << "Not integral" << std::endl;
}

// Note the order <U, T> is intentional
template< class T, class U> requires (are_same<U, T> && std::integral<T>)
void foo(T a, U b) {
    std::cout << "Integral" << std::endl;
}

int main() {
    foo(1, 2);
    return 0;
}

(我的目的是枚举包中每一个可能的有序类型对)

不幸的是,这段代码无法编译,编译器抱怨调用foo(int, int)不明确。我相信它认为are_same<U, T>are_same<T, U>不等同。我想知道为什么代码失败了我该如何修复它(以便编译器将它们视为等效)?

4

3 回答 3

6

来自cppreference.com Constraint_normalization

任何其他表达式 E 的范式是原子约束,其表达式为 E,其参数映射为恒等映射。这包括所有折叠表达式,甚至那些折叠在 && 或 || 上的表达式。运营商。

所以

template <typename... Types>
concept are_same = (... && same_with_others<Types, Types...>);

是“原子的”。

所以确实are_same<U, T>are_same<T, U>不等价。

我不知道如何实现它:-(

于 2019-11-06T09:10:34.957 回答
5

问题是,有了这个概念:

template <typename T, typename... Others>
concept are_same = (... && std::same_as<T, Others>);

这个概念的规范化形式是……就是这样。我们不能“展开”这个(无事可做),并且当前的规则不会通过概念的“部分”来规范化。

换句话说,要使其正常工作,您需要将您的概念规范化为:

... && (same-as-impl<T, U> && same-as-impl<U, T>)

进入:

... && (is_same_v<T, U> && is_same_v<U, T>)

并考虑一个折叠表达式&&约束包含另一个折叠表达式约束&&,如果其基础约束包含另一个的基础约束。如果我们有该规则,那将使您的示例有效。

将来可能会添加这个 - 但围绕包含规则的问题是我们不希望编译器全力以赴并实现完整的 SAT 求解器来检查约束包含。这个似乎并没有让它变得更复杂(我们实际上只是通过折叠表达式添加&&and||规则),但我真的不知道。

但是请注意,即使我们有这种折叠表达式的包含,are_same<T, U>仍然不会包含std::same_as<T, U>。它只会包含are_same<U, T>. 我不确定这是否可能。

于 2019-11-08T11:15:27.087 回答
0

churill 是对的。使用 std::conjunction_v 可能会有所帮助。

   template <typename T,typename... Types>
   concept are_same = std::conjunction_v<std::same_as<T,Types>...>;
于 2020-12-17T13:16:21.087 回答