C++20 要求表达式未捕获 static_assert

Kan*_*ame 5 c++ templates type-constraints c++-concepts c++20

当我第一次听说 C++20 约束和概念时,我真的很兴奋,到目前为止,我对它们进行了很多有趣的测试。最近,我想看看是否可以使用 C++20 概念来测试类或函数的约束。例如:

template <int N>
requires (N > 0)
class MyArray { ... };

template <int N>
concept my_array_compiles = requires {
  typename MyArray<N>;
};

my_array_compiles<1>;  // true
my_array_compiles<0>;  // false
Run Code Online (Sandbox Code Playgroud)

起初我没有遇到任何问题,但我遇到了一种情况,依赖函数中的 static_assert 会阻止编译,即使它出现在需要表达式中。下面是一个例子来说明这一点:

template <bool b>
requires b
struct TestA {
  void foo() {}
};

template <bool b>
struct TestB {
  static_assert(b);
  void foo() {}
};

template <template<bool> class T, bool b>
concept can_foo = requires (T<b> test) {
  test.foo();
};

can_foo<TestA, true>;   // true
can_foo<TestA, false>;  // false
can_foo<TestB, true>;   // true
// can_foo<TestB, false>; does not compile
Run Code Online (Sandbox Code Playgroud)

TestA 和 TestB 对于大多数用例来说应该类似地工作(尽管我发现 TestB<false> 只要没有实例化或取消引用就可以用作类型)。然而,我的期望是,require 表达式中失败的 static_assert 会导致其计算结果为 false。这对于使用仍然使用 static_assert 的库代码尤其重要。例如,std::tuple_element:

template <class T>
concept has_element_0 = requires {
    typename tuple_element_t<0, T>;
};

has_element_0<tuple<int>>;  // true
// has_element_0<tuple<>>; does not compile
Run Code Online (Sandbox Code Playgroud)

当我将空元组传递给上述概念时,我收到错误static_assert failed due to requirement '0UL < sizeof...(_Types)' "tuple_element index out of range"。我已经在 g++ 10.3.0 和 clang 12.0.5 上对此进行了测试。我能够通过提供一个使用约束的包装器来解决这个问题,但它在某种程度上违背了目的,因为我本质上是通过在更高级别强制执行相同的条件来阻止编译器看到 static_assert 。

template <size_t I, class T>
requires (I >= 0) && (I < tuple_size_v<T>)
using Type = tuple_element_t<I, T>;

template <class T>
concept has_element_0 = requires {
    typename Type<0, T>;
};

has_element_0<tuple<int>>;  // true
has_element_0<tuple<>>;     // false
Run Code Online (Sandbox Code Playgroud)

它并不总是有效,具体取决于 std::tuple_element 的使用方式:

template <size_t I, class T>
requires (I >= 0) && (I < tuple_size_v<T>)
tuple_element_t<I, T> myGet(const T& tup) {
    return get<I>(tup);
}

template <class T>
concept has_element_0 = requires (T tup) {
    myGet<0>(tup);
};

has_element_0<tuple<int>>;  // true
// has_element_0<tuple<>>; does not compile
Run Code Online (Sandbox Code Playgroud)

所以最终我的问题是:这种需要表达式的预期行为是否不考虑 static_assert ?如果是这样,这种设计的原因是什么?最后,是否有更好的方法可以在不使用上述解决方法的情况下使用 static_assert 实现我在类上的目标?

谢谢阅读。

Yak*_*ont 3

是的,您与之交互的内容中的任何内容都不会被检查。只是声明的直接上下文。

在某些情况下,使用 decltype 会检查某些构造的非直接上下文,但任何错误仍然很难。

这样做(很久以前)是为了减少对编译器的要求。只有在所谓的“直接上下文”中,编译器才需要能够在看到错误时干净地退出并继续编译。

静态断言永远不适合此目的。静态断言如果命中,则结束编译。