编译器是否可以决定忽略依赖类型上缺少的类型名,而 C++20 仍然需要它?

Can a compiler decide to ignore a missing typename on dependent type where it is still required by C++20?

提问人:Amir Kirsh 提问时间:2/14/2023 最后编辑:Amir Kirsh 更新时间:8/24/2023 访问量:283

问:

以下代码使用 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>;

MSVC 方法(不需要 typename for )似乎是合理的,因为 sizeof 适用于类型和变量。另一方面,GCC 和 Clang 似乎照本宣科,因为即使在 C++20 中,当上下文无法向编译器显示它是否将满足类型或变量时,您仍然需要这种情况。sizeoftypename

问题是这里是否允许 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

在上面的代码中,GCC 和 Clang 认为 A2 中的省略是非法的,因此无法实现这个概念,这不是编译错误,而是让概念获得布尔值 false ([temp.constr.atomic])。typename


C++ spec ([temp.res.general]) 列出了假定合格 id 为类型不需要的位置。运算符不在该列表中,因此似乎应该需要模板依赖类型。另一方面,在示例中不会显示为格式错误,不需要对缺失进行诊断(不在示例中并不能说明什么,但在某种程度上仍然保留了问题)。typenamesizeoftypenamesizeoftypename

为什么可以在不添加的情况下推断出正确的操作?主要是因为它不应该真正关心这是一个类型还是一个变量。而且规范没有具体说明这是格式不正确的。如果它确实说它格式不正确,那么指向此的指针应该是答案,这可能会使 MSVC 行为成为缺陷。sizeoftypename


顺便说一句,这并不能回答这个问题,可以通过以下代码使所有三个编译器都满意:

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;
C++ 语言-律师 C++20 类型名

评论

2赞 PaulMcKenzie 2/14/2023
无法直接回答您的问题,但 MSVC 似乎总是允许模板代码中缺少 。这可以追溯到 C++ 98。因此,Visual C++ 总是对此进行快速而松散的处理,而 g++ 则更加严格。typename
1赞 StoryTeller - Unslander Monica 2/14/2023
有趣。 不会改变 MSVC 的行为。/permissive-
0赞 PaulMcKenzie 2/14/2023
此外,顺便说一句,MS 似乎直到下一个主要编译器版本才解决这个问题。我遇到过这样的情况,即使用 VS 2015 编译良好的代码无法在 VS 2017、2019 等下编译,所有这些都是因为 VS 2015 中应该检测到的缺失。因此,如果我是你,我会对冲我的赌注并投入,正如 g++ 所建议的那样(这不会有什么坏处)。typenametypenametypename
1赞 Adrian Mole 2/14/2023
请注意您从在线标准草案中链接的段落中的最后三个词:“无需诊断”。
1赞 Language Lawyer 2/14/2023
@AdrianMole同样奇怪的是,该段落中的句子用句号分隔。

答:

1赞 Davis Herring 2/19/2023 #1

关于这一点,标准是非常间接的,但很明显,MSVC 在这里不符合。(在非 SFINAE/概念的情况下,只允许发出警告(“尽管缺少'typename',但 sizeof 的操作数被认为是一个类型”)并继续,但这不是重点。

请注意,即使在微不足道的情况下

struct X;
int y=X;

错误在于不能解释为 unqualified-id,因为它没有“适当声明”([expr.prim.id.unqual]/1)。在模板中,从属名称的语法解释是由 的存在与否决定的,尽管 qualified-ids 因此是在不知道它们的终端名称是否如此合适的情况下产生的;如果最终发现它们在这方面有所欠缺,我们显然必须拒绝它们。Xtypename