can*_*yme 19 c++ c++-concepts c++20
为什么 C++ 20 中使用 require 表达式的“Oneable”概念的某些实现无法在某些编译器上编译?
// `Oneable` implemented using `requires` with a simple assignment expression
// clang, gcc, and msvc all compile
template <class T>
concept Oneable = requires(T n) { n = 1; };
static_assert(Oneable<int>);
static_assert(!Oneable<int*>);
Run Code Online (Sandbox Code Playgroud)
// `Oneable` implemented using `requires` with an assignment statement inside
// the body of a lambda expression
// clang and msvc compile
// gcc gives a compiler error on instantiating Oneable<T*>
template <class T>
concept Oneable = requires { []() { T n = 1; }; };
static_assert(Oneable<int>);
static_assert(!Oneable<int*>);
Run Code Online (Sandbox Code Playgroud)
// `Oneable` implemented using `requires` with an instantiation of a struct
// gcc and msvc compile
// clang gives a compiler error on instantiating Oneable<int*>
template <class T>
struct S { T n = 1; };
template <class T>
concept Oneable = requires { S<T>{}; };
static_assert(Oneable<int>);
static_assert(!Oneable<int*>);
Run Code Online (Sandbox Code Playgroud)
gcc 第二个片段的输出:
<source>: In lambda function:
<source>:8:43: error: invalid conversion from 'int' to 'int*' [-fpermissive]
8 | concept Oneable = requires { []() { T n = 1; }; };
| ^
| |
|
Run Code Online (Sandbox Code Playgroud)
clang 第三个片段的输出:
<source>:8:18: error: cannot initialize a member subobject of type 'int *' with an rvalue of type 'int'
struct S { T n = 1; };
^
<source>:11:35: note: in instantiation of default member initializer 'S<int *>::n' requested here
concept Oneable = requires { S<T>{}; };
^
<source>:11:30: note: in instantiation of requirement here
concept Oneable = requires { S<T>{}; };
^~~~~~
<source>:11:19: note: while substituting template arguments into constraint expression here
concept Oneable = requires { S<T>{}; };
^~~~~~~~~~~~~~~~~~~~
<source>:15:18: note: while checking the satisfaction of concept 'Oneable<int *>' requested here
std::cout << Oneable<int*> << '\n';
^~~~~~~~~~~~~
Run Code Online (Sandbox Code Playgroud)
使用的编译器是 x86-64 gcc 12.2、x86-64 clang 15.0.0 和 x64 msvc 19.33。
除了设置语言标准所需的标志之外,没有给出任何标志。
Bar*_*rry 21
这里的问题与直接上下文有关。这里发挥作用的技术是 SFINAE - 替换失败不是错误。但这并不是所谓的替换的直接上下文中的错误- 不幸的是,这个术语基本上没有很好的定义。
不过,基本上,函数模板的实际签名是直接上下文,但函数模板的主体不是。函数体中的任何错误都是硬编译器错误,而不是可优雅检查的错误。所以这个版本:
template <class T>
concept Oneable = requires { []() { T n = 1; }; };
Run Code Online (Sandbox Code Playgroud)
不会起作用 - 失败发生T=int*在 lambda 体中。这超出了表达式的直接上下文。
这个版本:
template <class T>
struct S { T n = 1; };
template <class T>
concept Oneable = requires { S<T>{}; };
Run Code Online (Sandbox Code Playgroud)
很难提供明确的答案,因为同样没有明确说明直接上下文。这个可能应该能够工作(以 lambda 情况永远不会的方式),例如 gcc 只是使默认成员初始值设定项成为直接上下文的一部分,因为这是最用户友好的选项。否则你最终会得到这样的结果:
template <typename T>
struct A {
T var = T();
A() = default;
};
template <typename T>
struct B {
T var = T();
B() requires std::default_initializable<T> = default;
};
Run Code Online (Sandbox Code Playgroud)
B显然是正确的,但是必须写下来有点荒谬,所以如果A也正确的话那就太好了。
如果您想在所有编译器中保持可靠,现在最好的办法就是尽量避免像这样的模棱两可的情况。编写代码喜欢B,不喜欢A。
| 归档时间: |
|
| 查看次数: |
1281 次 |
| 最近记录: |