【问题标题】:An invalid default member initializer that is never used从未使用过的无效默认成员初始化程序
【发布时间】:2020-01-06 14:39:14
【问题描述】:

请考虑以下代码:

template <typename T>
struct Test
{
    Test() = default;

    explicit Test(const T& arg)
     : m_member(arg)
    {
    }

    T     m_member{};
};

int main()
{
    Test<int>       t1;

    int             v2 = 34;
    Test<int&>      t2(v2);     // (!)

    return 0;
}

上面的代码是否应该编译并且没有未定义的行为?

标记为 (!) 的行使用引用类型的参数实例化类模板 Test。在这种情况下,成员 Test::m_member 的默认初始化程序无效(嗯,引用 必须 用某个对象进行初始化)。但另一方面,默认构造函数(唯一可以使用该默认初始化程序的构造函数)从未在程序中使用,因此不应实例化。

在 C++11 下,编译器是否允许/要求尝试/跳过实例化构造函数中使用的成员的默认初始值设定项的实例化(即程序不实例化任何可能需要该初始化程序的构造函数)?

【问题讨论】:

标签: c++ c++11 language-lawyer


【解决方案1】:

幸运的是,这没关系。

但另一方面,默认构造函数(唯一可以使用该默认初始化程序的构造函数)从未在程序中使用,因此不应实例化它。

确实:

[temp.mem.func]/2

类模板的成员函数的模板参数由调用该成员函数的对象类型的模板参数确定。

由于在您的示例中,Test&lt;int&amp;&gt;::Test() 没有被调用,它的模板参数没有确定,它的构造不会使程序格式错误。

【讨论】:

    【解决方案2】:

    上面的代码是否应该编译并且没有未定义的行为?

    是的,我看不出有什么理由不应该这样做。

    在 C++11 下,编译器是否允许/要求尝试/跳过 未使用成员的默认初始值设定项的实例化 在实例化的构造函数中(即程序不实例化 任何可能需要该初始化程序的构造函数?

    我没有 C++11 规范,但目前的草案说:

    [default.ctor-2.3]

    X的默认默认构造函数在以下情况下被定义为已删除:
    任何没有默认成员初始值设定项 ([class.mem]) 的非静态数据成员都是引用类型,

    既然你确实有一个默认的成员初始化器(T m_member{};),它不会delete你的默认默认构造函数,以防你在T是引用类型时尝试使用它。

    它会编译失败:
    所有输出都来自使用-std=c++11

    g++: error: cannot bind non-const lvalue reference of type ‘int&’ to an rvalue of type ‘int’
    clang++ error: non-const lvalue reference to type 'int' cannot bind to an initializer list temporary
    

    尝试通过删除默认成员初始化器来修复它:

       11 |     T     m_member;
    

    导致需要删除:

    21:16: error: use of deleted function ‘Test<T>::Test() [with T = int&]’
       21 |     Test<int&> x;
          |                ^
    4:5: note: ‘Test<T>::Test() [with T = int&]’ is implicitly deleted because the default definition would be ill-formed:
        4 |     Test() = default;
          |     ^~~~
    4:5: error: uninitialized reference member in ‘struct Test<int&>’
    

    【讨论】:

      【解决方案3】:

      这在CWG Issue 1396中有描述

      非静态数据成员初始化器的延迟实例化和检查

      部分:17.8.1 [temp.inst] 状态:起草提交者:Jason Merrill 日期: 2011-09-22

      非静态数据成员初始化器获得与 成员函数和默认参数,但它们是否也被实例化了 像他们一样需要吗?什么时候检查它们的有效性?

      2012 年 10 月会议记录:

      CWG 同意应处理非静态数据成员初始化程序 像默认参数一样。

      如您所见,共识是默认成员初始化器被视为函数的默认参数。因此,我们可以检查默认参数的行为,以确定 CWG 打算如何处理此问题。我们可以看到那些没有与类定义一起实例化:

      [temp.inst]/1

      类模板特化的隐式实例化导致 声明的隐式实例化,但不是 类成员函数的定义或默认参数, 成员类、作用域成员枚举、静态数据成员和 成员模板;

      如果以使用它们的方式调用构造函数,它们被实例化,与本段中的成员函数 f 相同

      [temp.inst]/12

      如果函数模板 f 以需要默认值的方式调用 要使用的参数,查找依赖名称,语义 检查约束,并在中使用的任何模板的实例化 默认参数的完成就像默认参数是 函数模板特化中使用的初始化器 范围,相同的模板参数和相同的访问权限 那时使用的函数模板 f。这种分析称为 默认参数实例化。实例化的默认参数是 然后作为f的参数。

      【讨论】:

        猜你喜欢
        • 2015-03-22
        • 2019-09-10
        • 1970-01-01
        • 2012-08-29
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        相关资源
        最近更新 更多