【问题标题】:Which substitution failures are not allowed in requires clauses?requires 子句中不允许哪些替换失败?
【发布时间】:2020-04-20 15:54:45
【问题描述】:

如果所有模板参数都是唯一的,我正在尝试编写一个返回 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&lt;int&gt;’ invalid

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

将模板参数替换为模板实体声明中使用的 requires 表达式可能会导致在其需求中形成无效类型或表达式,或违反这些需求的语义约束。在这种情况下, requires-expression 的计算结果为 false,不会导致程序格式错误。

clang 和 gcc 错了吗?还是 cppreference 错了?或者(很可能)我读错了?

requires 表达式中不允许出现哪些类别的替换失败?请参阅当前 C++20 草案的相应部分。

【问题讨论】:

  • 硬错误与软错误...
  • 您能否指出标准草案中requires 表达式的这种区别?

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


【解决方案1】:

在 requires 表达式中不允许出现哪些类别的替换失败?

和其他地方一样。这实际上并不特定于概念。这是您的示例的略微修改版本,它演示了 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 中引入的一个术语,但并未真正彻底定义

如果替换导致无效的类型或表达式,则类型推导失败。无效类型或表达式是一种格式错误的类型或表达式,如果使用替换参数编写,则需要进行诊断。 [ 注意:如果不需要诊断,程序仍然是不正确的。访问检查是替换过程的一部分。 — end note ] 只有在函数类型、其模板参数类型及其 explicit-specifier 的直接上下文中的无效类型和表达式会导致推导失败。 [注意:对类型和表达式的替换可能会导致类模板特化和/或函数模板特化的实例化、隐式定义函数的生成等效果。此类效果不在“直接上下文”中,并且可能导致在程序格式不正确。 ——尾注]

一般的想法是,只有声明中的失败会导致替换失败,而定义中的失败会导致硬错误导致程序出错-形成。在此处的示例中,问题不在于 Test&lt;int, int&gt; 的声明,而是在它的定义中(在我们到达其基类的地方)。这被认为太晚了——直接上下文之外的失败不再是替换失败。


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

【讨论】:

  • 感谢您的解释和核心问题的链接!由于这种模糊性,我很惊讶 SFINAE 在编译器中的工作一致性......
猜你喜欢
  • 2022-06-22
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2011-10-30
  • 2021-08-24
  • 1970-01-01
  • 2022-07-04
  • 1970-01-01
相关资源
最近更新 更多