如何实现与参数顺序无关的std :: same_as的通用形式(即,用于两个以上类型的参数)?

Kae*_*Rin 7 c++ c++-concepts fold-expression c++20

背景

我们知道该概念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;
}
Run Code Online (Sandbox Code Playgroud)

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

不幸的是,该代码无法编译,编译器抱怨对调用foo(int, int)的歧义。我相信,它认为are_same<U, T>are_same<T, U>不等价的。我想知道为什么代码无法解决该问题(以便编译器将它们视为等同)?

Jar*_*d42 5

来自cppreference.com Constraint_normalization

任何其他表达式E的范式是原子约束,其表达式为E且参数映射为身份映射。这包括所有折叠表达式,甚至那些在&&或||上折叠的表达式 操作员。

所以

template <typename... Types>
concept are_same = (... && same_with_others<Types, Types...>);
Run Code Online (Sandbox Code Playgroud)

是“原子的”。

因此,我们确实are_same<U, T>are_same<T, U>不相等。

我看不到如何实现它:-(


Bar*_*rry 5

问题是,用这个概念:

template <typename T, typename... Others>
concept are_same = (... && std::same_as<T, Others>);
Run Code Online (Sandbox Code Playgroud)

是这个概念的规范化形式就是...完全是那样。我们无法“展开”此操作(无事可做),并且当前规则无法通过概念的“部分”进行规范化。

换句话说,您需要将其标准化为:

... && (same-as-impl<T, U> && same-as-impl<U, T>)
Run Code Online (Sandbox Code Playgroud)

变成:

... && (is_same_v<T, U> && is_same_v<U, T>)
Run Code Online (Sandbox Code Playgroud)

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

将来可能会添加它-但是关于包含规则的问题是我们不希望编译器全力以赴并实现完整的SAT求解器来检查约束包含。这个函数似乎并没有变得那么复杂(我们实际上只是通过fold-expressions 添加了&&and ||规则),但是我真的不知道。

但是请注意,即使我们具有这种fold-expression包含,are_same<T, U>也仍然不会包含std::same_as<T, U>。它只会服从are_same<U, T>。我不确定这是否可能。

  • [this](https://godbolt.org/z/35YJAa)无效,这令我感到惊讶。可以仅包含概念是合理的。但是,在我看来,`(... &amp;&amp; C &lt;T&gt;)`不包含* concept *`C &lt;T&gt;`会让很多用户感到惊讶。 (2认同)