Ami*_*rsh 13 c++ typename language-lawyer c++20
以下代码可以使用 MSVC 进行编译,但在 GCC 和 Clang 中会失败,因为typename在依赖类型之前缺少:
struct Foo { struct value{ }; };
struct Bar { int value; };
template<typename T>
constexpr size_t SIZE = sizeof(T::value); // missing typename here, for Foo
constexpr size_t s1 = SIZE<Foo>; // missing typename before dependent type above
constexpr size_t s2 = SIZE<Bar>;
Run Code Online (Sandbox Code Playgroud)
MSVC 方法不需要 typename sizeof,这似乎是合理的,因为 sizeof 对类型和变量都适用。另一方面,GCC 和 Clang 似乎按规矩办事,因为这是typename 即使在 C++20 中您仍然需要的情况之一,当上下文无法向编译器显示它是否会满足类型或多变的。
问题是这里MSVC是否允许宽容,即编译器如果不加 也能正确执行所需的操作typename,是否允许这样做?还是与规范相矛盾?
MSVC 与 Clang 和 GCC 的方法之间的差异在于以下代码中的实际差异,该代码由所有三个编译器编译,但行为不同:
template<typename T> concept A1 = sizeof(typename T::value) > 0;
template<typename T> concept A2 = sizeof(T::value) > 0;
struct Foo { struct value{ }; };
constexpr bool v1 = A1<Foo>; // true with all
constexpr bool v2 = A2<Foo>; // true with MSVC, false with GCC and Clang
Run Code Online (Sandbox Code Playgroud)
在上面的代码中,GCC 和 Clang 认为 A2 中的省略typename是非法的,因此失败了这个概念,这不是编译错误,而是概念得到了布尔值 false ( [temp.constr.atomic] )。
C++20 规范 ( [temp.res.general]typename ) 列出了不需要假设qualified-id 是类型的地方。该sizeof运算符不在该列表中,因此似乎它应该需要typename模板依赖类型。另一方面,在示例sizeof中并没有出现格式错误,不需要诊断缺失(不在示例中并没有说明任何内容,但在某种程度上仍然保留了问题)。typename
为什么sizeof 可以允许推断正确的操作而不添加typename?主要是因为它不应该真正关心这是类型还是变量。并且规范并没有明确指出这是不正确的。如果它确实说它格式错误,那么答案应该是指向此的指针,这可能会使 MSVC 行为成为缺陷。
附带说明一下,这并不能回答问题,可以让所有三个编译器都对以下代码感到满意:
template<typename T>
constexpr size_t SIZE = sizeof(T::value);
template<typename T> requires requires { typename T::value; }
constexpr size_t SIZE<T> = sizeof(typename T::value);
Run Code Online (Sandbox Code Playgroud)
对于这个概念:
template<typename T> concept A =
sizeof(T::value) > 0 ||
sizeof(typename T::value) > 0;
Run Code Online (Sandbox Code Playgroud)
标准对于这一点非常间接,但是\xe2\x80\x99s很清楚MSVC不符合标准标准。(在非 SFINAE/概念情况下,将允许仅发出警告(尽管缺少 \xe2\x80\x98typename\xe2\x80\x99\xe2\x80\ x9d) 并继续,但是 \xe2\x80\x99s 并不是重点。)
\n请注意,即使在微不足道的情况下
\nstruct X;\nint y=X;\nRun Code Online (Sandbox Code Playgroud)\n错误是Xcan\xe2\x80\x99t 被解释为unqualified-id,因为它没有 \xe2\x80\x9c 适当声明\xe2\x80\x9d\xc2\xa0([expr.prim.id.unqual]/ 1)。在模板中,从属名称的语法解释是通过存在或不存在来确定的typename,尽管qualified-id在不知道其终端名称是否如此适当地声明的情况下生成了如果最终发现它们在这方面存在缺陷,我们显然必须拒绝它们。