支持模板结构的初始化

时间:2018-02-03 17:53:37

标签: 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);
}

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

  

错误:无法将'&lt; brace-enclosed initializer list&gt;'中的'{nullptr,{args#0}}'转换为'Node&lt; int&gt;'

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

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

2 个答案:

答案 0 :(得分:3)

这是一个GCC错误。

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

  

否则,如果初始化列表具有E类型的单个元素且T不是引用类型或其引用类型与E引用相关,则从该元素初始化对象或引用(通过复制初始化为copy-list-initialization,或直接初始化直接列表初始化);如果需要缩小转换(见下文)将元素转换为T,则程序格式不正确。   [实施例:

int x1 {2};                         // OK
int x2 {2.0};                       // error: narrowing
     

- 结束示例]

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

  

聚合是一个数组或带有

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

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

  •   
  • 没有虚拟功能,

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

  •   

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

  

否则,该元素是从相应的 initializer-clause 或相应的指定初始化器的 brace-or-equal-initializer 复制初始化的 - 条款。如果该初始化程序的格式为 assignment-expression 或= assignment-expression ,并且转换表达式需要缩小转换,则程序格式错误。 [注意:如果初始化程序本身是初始化程序列表,则该元素是列表初始化的,如果该元素是聚合,则将导致本子条款中规则的递归应用。 - 结束说明]

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

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

答案 1 :(得分:1)

我相信你想要这样的东西:

return {nullptr, T{args...}};

这使用提供的参数显式构造对象T,并且也适用于任何用户定义的类型,如下所示

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

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

struct Foo {
    string s;
    int i;
};

int main() {
    auto n = create<Foo>("foo", 42);
    cout << n.val.s << ' ' << n.val.i << endl;
}