带有和不带有参数列表的类模板实例的`extern`声明和后续定义

时间:2018-09-26 10:16:36

标签: c++ templates language-lawyer c++17

考虑以下代码:

template <typename T = int> struct X {};
extern X foo;
X foo;

Live on gcc.godbolt.org

我希望它的格式正确,但是GCC,Clang和MSVC拒绝它并显示以下错误消息:

GCC 8.2(带有-std=c++17 -Wall -Wextra -pedantic-errors):

<source>:3:3: error: conflicting declaration 'X foo'
 X foo;
   ^~~
<source>:2:10: note: previous declaration as 'X<int> foo'
 extern X foo;
          ^~~

Clang 7.0.0(带有-std=c++17 -Wall -Wextra -pedantic-errors):

<source>:2:10: error: declaration of variable 'foo' with deduced type 'X' requires an initializer
extern X foo;
         ^

MSVC Pre 2018(带有/std:c++latest):

<source>(2): error C2641: cannot deduce template argument for 'X'
<source>(3): error C2133: 'foo': unknown size
<source>(3): error C2641: cannot deduce template argument for 'X'

现在有趣的部分。

此代码段被Clang接受,但被GCC和MSVC拒绝:

extern X<> foo;
X foo;

此代码段已被GCC接受,但被Clang和MSVC拒绝:

extern X foo;
X<> foo;

所有三个编译器都接受此代码:

extern X<> foo;
X<> foo;

最后,这个被GCC和Clang接受,但被MSVC拒绝:

X foo;

这是怎么回事?这五个摘要中的哪一个是正确的?

1 个答案:

答案 0 :(得分:3)

五个片段中的

clang都是正确的。

extern X<> foo; // type: X<>
X foo; // CTAD => type: X<>

foo仅是foo的声明,然后在以后使用相同的类型重新声明,因此gcc和MSVC拒绝这种情况是错误的。

extern X foo; // invalid
X<> foo; // ok => type: X<>

gcc在这里是错误的。 foo不是定义,只是没有初始化程序的声明,因此也不是CTAD(initializing declaration)所必需的[dcl.class.type.deduct]

extern X<> foo; // type: X<>
X<> foo; // type: X<>

这类似于1),仅不需要CTAD。由于第二个foo只是foo的重新声明,并且同时使用相同的类型对其进行定义,因此它是有效的。

X foo;

这里的MSVC是错误的,该标准在全球范围内对CTAD没有任何限制。

总而言之,我们可以推断出您的原始代码段格式错误,因为您将CTAD用于未定义的变量。