【问题标题】:Brace initialization of template struct模板结构的大括号初始化
【发布时间】:2018-07-13 23:49:30
【问题描述】:

我正在尝试创建一个链表模板,它适用于用户定义的类型,但对于 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 会生成以下错误消息。

错误:无法将“{nullptr, {args#0}}”从“”转换为“Node

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

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

【问题讨论】:

  • 移除大括号 {args...} 解决了 GCC Clang的问题。
  • 成功了:return {nullptr, args... };
  • 如果我移除大括号,那么我将无法使用用户定义的构造函数来处理结构。 (godbolt.org/g/32ZR6K) 我知道通用解决方案,但我对正确的编译器行为应该是什么感兴趣。
  • 建议您添加 [language-lawyer] 标签。
  • GCC 不允许struct { int x; } v = { {1} };,尽管它允许int x = {1};。我认为这是 GCC 的错误。

标签: c++ gcc clang language-lawyer


【解决方案1】:

这是一个 GCC 错误。

首先,根据[dcl.init.list]/3.9,标量初始化器(标量类型的列表初始化)周围的大括号是允许的:

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

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

— 结束示例 ]

第二,Node&lt;int&gt;是根据[dcl.init.aggr]/1的聚合:

聚合是一个数组或一个类

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

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

  • 没有虚函数,并且

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

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

否则,元素从相应的initializer-clause或相应designated-initializer-的brace-or-equal-initializer复制初始化条款。如果该初始值设定项的形式为 assignment-expression 或 = assignment-expression 并且需要缩小转换来转换表达式,则程序是非良构的。 [ 注意:如果初始化器本身是初始化器列表,则元素是列表初始化的,如果元素是聚合,这将导致本子条款中的规则的递归应用。 — 尾注 ]

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

作为结论,这个初始化是明确定义的。

【讨论】:

    【解决方案2】:

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

    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;
    }
    

    【讨论】:

      猜你喜欢
      • 2014-07-31
      • 2013-08-23
      • 2015-08-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2020-06-02
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多