以下code使用MSVC编译,但由于依赖类型前缺少typename
,因此使用GCC和Clang编译失败:
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>;
MSVC方法不需要sizeof
的typename,这看起来是合理的,因为sizeof对类型和变量都有效。另一方面,GCC和Clang看起来是照章办事,因为这是一种仍然需要typename
even in C++20的情况,当上下文不能向编译器显示它是要满足类型还是变量时。
问题是这里是否允许MSVC是许可的,也就是说,如果编译器可以在没有typename
的情况下正确地执行所需的操作,那么它是否被允许这样做?或者它是否与规范相矛盾?
MSVC与Clang和GCC的方法之间的差异体现在以下code中的实际差异上,code由所有三个编译器编译,但行为不同:
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
在上面的代码中,GCC和Clang认为A2中typename
的省略是非法的,因此使概念失败,这不是编译错误,而是概念得到布尔值false([temp.constr.atomic])。
C++20规范([temp.res.general])列出了假定限定id是一个类型时不需要typename
的地方。sizeof
操作符不在该列表中,因此对于模板依赖类型,它似乎需要typename
。另一方面,sizeof
在示例中没有显示为 *ill-formed,对于缺少typename
,不需要诊断 *(不在示例中并不说明什么,但在某种程度上仍然保留问题)。
为什么sizeof
* 可以 * 允许推断正确的操作而不添加typename
?主要是因为它不应该真正关心这是一个类型还是一个变量。并且规范没有明确指出这是一个格式错误。如果它确实指出这是一个格式错误,那么指向它的指针应该是答案,这可能会使MSVC行为成为一个缺陷。
顺便说一句,这并没有回答这个问题,我们可以让所有三个编译器都满意with the following code:
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);
template<typename T> concept A =
sizeof(T::value) > 0 ||
sizeof(typename T::value) > 0;
1条答案
按热度按时间uxhixvfz1#
标准在这一点上非常间接,但是很明显MSVC在这里不符合(在非SFINAE/concept的情况下,只允许发出一个警告(“sizeof的操作数被认为是一个类型,尽管缺少'typename'”)并继续,但这不是真正的要点。
请注意,即使在的平凡情况下
错误在于
X
不能被解释为 unqualified-id,因为它没有被“适当地声明”([expr.prim.id.unqual]/1)。在模板中,语法解释由typename
的存在或不存在来固定,尽管事实上 qualified-id 由此被产生而不知道它们的终端名称是否被如此适当地声明;如果最终发现它们在这方面有所欠缺,我们显然必须拒绝它们。