c++ - 哪些替换失败在 requires 子句中是不允许的?

标签 c++ language-lawyer c++20 c++-concepts

如果所有模板参数都是唯一的,我正在尝试编写一个返回 true 的函数。

伪代码:

template<typename... Ts>
auto types_are_unique() -> bool {
  return (no two Ts are the same);
}

而不是“手动”比较每个 T与其他人一样,我想利用这样一个事实,即如果两个或多个基类相同,则不允许多重继承。
#include <utility>

template <typename T>
struct X {};

template <typename... Ts>
struct Test : X<Ts>... {};

template <typename... Ts>
constexpr auto types_are_unique() -> bool {
  return false;
}

template <typename... Ts>
requires requires { Test<Ts...>{}; }
constexpr auto types_are_unique() -> bool {
  return true;
}

int main() {
  static_assert(types_are_unique<int, float>()); // compiles
  static_assert(not types_are_unique<int, int>()); // fails
}

gcc 和 clang 都同意这无法编译,因为 unique.cpp:7:8: error: duplicate base type ‘X<int>’ invalid .

这令人惊讶,阅读 https://en.cppreference.com/w/cpp/language/constraints#Requires_expressions

The substitution of template arguments into a requires-expression used in a declaration of a templated entity may result in the formation of invalid types or expressions in its requirements, or the violation of semantic constraints of those requirements. In such cases, the requires-expression evaluates to false and does not cause the program to be ill-formed.



clang 和 gcc 错了吗?还是 cppreference 错误?或者(很可能)我读错了吗?
requires中不允许出现哪些类的替换失败表达?请参阅 C++20 当前草案的相应部分。

最佳答案

Which classes of substitution failures are not allowed to occur in requires expressions?



和其他地方一样。这实际上并不是特定于概念的。这是您示例的稍微修改版本,它演示了 C++11 的相同问题:
using size_t = decltype(sizeof(0));

template <typename T>
struct X {};

template <typename... Ts>
struct Test : X<Ts>... {};

template <typename...> struct typelist { };

template <typename... T, size_t = sizeof(Test<T...>)>
constexpr auto types_are_unique(typelist<T...>) -> bool {
    return true;
}
constexpr auto types_are_unique(...) -> bool {
    return false;
}

// ok
static_assert(types_are_unique(typelist<int, float>()));

// compile error
static_assert(not types_are_unique(typelist<int, int>()));

问题与在替代的直接上下文中存在和不存在的内容有关。这是在 [temp.deduct]/8 中引入的术语但并没有真正彻底定义†:

If a substitution results in an invalid type or expression, type deduction fails. An invalid type or expression is one that would be ill-formed, with a diagnostic required, if written using the substituted arguments. [ Note: If no diagnostic is required, the program is still ill-formed. Access checking is done as part of the substitution process. — end note ] Only invalid types and expressions in the immediate context of the function type, its template parameter types, and its explicit-specifier can result in a deduction failure. [ Note: The substitution into types and expressions can result in effects such as the instantiation of class template specializations and/or function template specializations, the generation of implicitly-defined functions, etc. Such effects are not in the “immediate context” and can result in the program being ill-formed. — end note ]



一般的想法是只有声明中的失败会导致替换失败,而定义中的失败会导致导致程序格式错误的硬错误。在这里的例子中,问题不在于 Test<int, int> 的声明它在它的定义中(在我们到达它的基类的地方)。这被认为为时已晚——直接上下文之外的失败不再是替代失败。

†我们甚至有一个核心问题 ( 1844 ) 要求更好的定义。

关于c++ - 哪些替换失败在 requires 子句中是不允许的?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/59555623/

相关文章:

c++ - 运算符如何重载|专门实现了 C++ 范围适配器?

c++ - 浮点常量的编译时转换

c++ - OpenCV 中的自定义实现

c++ - 如何配置 Emacs 以突出显示违反详细代码样式的 C++?

c++ - 是否需要实现来诊断对同一TU中相同显式专业的重复定义进行ODR违规?

c++ - 签名/未签名别名规则是否按预期工作?

c++ - 是否存在考虑所有元素的 ranges::views::group_by 对应物,而不是仅仅考虑连续的元素?

c++ - 使用 Objective-C (Cocoa) 事件 (performSelector) 传递 C++ 对象

c++ - "using"类作用域的类型别名 : [when] can usages in methods PRECEDE the type alias?

c++ - C++中各种类型的任意嵌套可迭代实现的求和函数