支持模板结构的初始化

Ste*_*ven 11 c++ gcc clang language-lawyer

我正在尝试创建一个链表模板,它适用于用户定义的类型,但对于基本类型,如gcc和clang的int行为不同.

template<class T>
struct Node {
  Node* next;
  T val;
};

template<class T, class... Args>
Node<T> create(Args... args) {
  return {nullptr, {args...}};
}

int main() {
  create<int>(0);
}
Run Code Online (Sandbox Code Playgroud)

虽然clang编译代码没有问题,但gcc会生成以下错误消息.

错误:无法将'{nullptr,{args#0}}'从'<brace-enclosed initializer list>'转换为'Node <int>'

虽然我知道如何解决这个问题,但我仍然感兴趣的是clang是否过于宽松,我不能依赖这段代码的可移植性,或者它是一个gcc bug,应该在某个时候解决.

示例:https://godbolt.org/g/9gnvNQ

xsk*_*xzr 4

这是一个 GCC 错误。

\n\n

首先,根据[dcl.init.list]/3.9 ,允许在标量初始值设定项周围使用大括号(标量类型的列表初始化) :

\n\n
\n

否则,如果初始化器列表具有类型 E 的单个元素,并且 T 不是引用类型或其引用类型与 E 引用相关,则从该元素初始化对象或引用(通过复制列表的复制初始化) -初始化,或通过直接初始化进行直接列表初始化);如果需要缩小转换(见下文)将元素转换为 T,则该程序格式错误。\n [\xe2\x80\x89示例:

\n\n
int x1 {2};                         // OK\nint x2 {2.0};                       // error: narrowing\n
Run Code Online (Sandbox Code Playgroud)\n\n

\xe2\x80\x94\xe2\x80\x89结束示例\xe2\x80\x89]

\n
\n\n

其次,Node<int>是根据[dcl.init.aggr]/1 的聚合:

\n\n
\n

聚合是一个数组或一个类

\n\n
    \n
  • 没有用户提供的、显式的或继承的构造函数([class.ctor]),

  • \n
  • 没有私有或受保护的非静态数据成员([class.access]),

  • \n
  • 没有虚函数,并且

  • \n
  • 没有虚拟、私有或受保护的基类 ([class.mi])。

  • \n
\n
\n\n

因此,聚合初始化被执行,并根据[dcl.init.aggr]/4.2val递归地进行列表初始化:{args...}

\n\n
\n

否则,该元素是从相应的初始化子句或相应的指定初始化子句的大括号或等于初始化器复制初始化的。如果该初始值设定项的形式为赋值表达式或 =赋值表达式,并且需要缩小转换来转换该表达式,则该程序格式错误。[\xe2\x80\x89注意:如果初始化器本身是初始化器列表,则该元素是列表初始化的,如果该元素是聚合,则这将导致递归应用本子条款中的规则。\xe2\x80\x94\xe2\x80\x89尾注\xe2\x80\x89]

\n
\n\n

然后[dcl.init.list]/3.9再次应用。

\n\n

总之,这个初始化是明确定义的。

\n