【问题标题】:Reference initialization forms参考初始化表格
【发布时间】:2014-11-23 14:37:08
【问题描述】:

所以我正在测试一些参考初始化表单,描述为here。我想知道什么时候:

T & ref = { arg1, arg2, ... };

T && ref = { arg1, arg2, ... };

表单将永远被使用,究竟是为了什么。我想这是用'initializer_list'初始化临时数组和构造函数,像这样:

int main()
{


    struct _ab
    {
        _ab() {cout << "_ab()" << endl;}

        _ab(initializer_list<int> iArr) : a(*iArr.begin()), b(*iArr.end()) {cout << "_ab()" << endl;}

        ~_ab() {cout << "~_ab()" << endl;}
        int a, b;
    };
    const _ab & i = {1, 2};

    cout << i.a << endl;

    return 0;
}

在这种情况下,我尝试使用这样的默认构造函数使用临时“_ab”对象初始化 const 引用:

int main()
    {


        struct _ab
        {
            _ab() {cout << "_ab()" << endl;}

            _ab(initializer_list<int> iArr) : a(*iArr.begin()), b(*iArr.end()) {cout << "_ab()" << endl;}

            ~_ab() {cout << "~_ab()" << endl;}
            int a, b;
        };
        const _ab & i(); // error 1

        cout << i.a << endl; // error 2

        return 0;
    }

但是这个例子没有编译,有 2 个错误:

错误 1:'const main()::_ab& i()',使用本地类型 'const 声明 main()::_ab', 被使用但从未定义 [-fpermissive]|

错误2:请求'i'中的成员'a',它是非类类型 'const main()::_ab&()'|

你能告诉我上面 2 的构造到底是什么意思吗?

编辑:我理解第二个示例的问题。它是在声明一个函数而不是变量。但是仍然有人可以解释一下为什么引用可以使用初始化列表进行初始化以及它的用途是什么?

【问题讨论】:

  • 我已经编辑了我的答案。
  • 切线地,_ab 是一个奇怪的类型名称...ab 有什么问题?

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


【解决方案1】:
const _ab & i();

标准在 [dcl.init]/8 的注释中对此进行了解释:

[ 注意:因为()是初始化器的语法不允许的,

X a();

不是X类对象的声明,而是声明 不带参数并返回 X 的函数。

这适用于任何类型,被称为最令人烦恼的解析。当然i.a 是格式错误的。


但是仍然有人可以解释一下为什么引用有可能 用初始化列表进行初始化,它是用来做什么的?

[dcl.init]/3:

T 类型的对象或引用的列表初始化定义如下:

  • [..]

  • 否则,如果初始值设定项列表具有 E 类型的单个元素,并且 T 不是引用类型或其引用类型是 与E相关的引用,对象或引用从初始化 该元素;如果需要缩小转换(见下文) 将元素转换为 T,程序格式错误。

  • 否则,如果T 是引用类型,则由 T 引用的类型的纯右值临时是复制列表初始化或 直接列表初始化,取决于初始化的类型
    [ 注意: 像往常一样,如果 引用类型是对非常量类型的左值引用。 ——结束注释 ]

最后一个注释是指这样一个事实

int& i = {1};

格式不正确,因为我们必须将 1 绑定到非常量左值引用,这是不可能的。也不会

std::string& s = {20, '5'};

是有效的,因为我们必须用一个临时值来初始化一个非常量左值引用。
然后标准给出了例子:

struct S {
    S(std::initializer_list<double>); // #1
    S(const std::string&);            // #2
    // ...
};

const S& r1 = { 1, 2, 3.0 };    // OK: invoke #1
const S& r2 { "Spinach" };      // OK: invoke #2
S& r3 = { 1, 2, 3 };            // error: initializer is not an lvalue
const int& i1 = { 1 };          // OK
const int& i2 = { 1.1 };        // error: narrowing
const int (&iar)[2] = { 1, 2 }; // OK: iar is bound to temporary array

这对于函数调用也特别有用。

void f(std::vector<int> const&);

f( {first, last} );

【讨论】:

    【解决方案2】:

    const _ab &amp; i();

    上面的代码声明了(并调用)一个函数,它返回一个对_ab结构的const引用——这是第一个错误。第二个错误是你试图访问一个函数的成员变量(i),这是不可能的。

    要使用默认构造函数,您可以使用新的 {...} C++11 语法,它应该可以在您的示例中使用

    const _ab & i {}; // probably will work fine
    

    但是省略大括号会导致错误 - 这将不起作用:

    const _ab & i;  // error
    

    这也应该可以正常工作:

    const _ab & i = {};
    

    编辑:

    只有当它是 const 引用时,您才能使用“匿名”对象(就像在所有示例中一样)初始化引用。它在语言中是允许的,我不确定是否有任何更深层次的原因以及为什么不应该允许它。因此,您实际上并没有使用初始化列表(或其他)初始化引用 - 这些用于初始化匿名对象,并且此匿名对象用作 const 引用的“目标”。如果您将构造函数声明为explicit,则需要指定类型,因此在这种情况下,对于实际使用的初始化程序而言,它可能不会那么令人困惑:

    const _ab & i = _ab{/*...*/};
    

    这相当于:

    const _ab anonymousObject = _ab{/*...*/};
    const _ab & i = anonymousObject;
    

    【讨论】:

    • 所以实际上那些初始化引用的初始化列表形式是用来用它们创建一个临时对象的。
    【解决方案3】:

    您看到的情况类似于允许将右值绑定到 const 引用的情况,例如

    std::string str() { return "blah"; }
    
    const std::string& s = str();
    

    函数返回的纯右值被绑定到引用,并且它的生命周期被延长,直到引用的生命周期结束。

    执行相同规则的另一种方法是直接创建一个临时对象:

    const std::string& s = std::string();
    

    这会创建一个绑定到引用的临时对象(将其生命周期延长到引用的生命周期)。

    const _ab&amp; i = {1, 2} 的情况非常相似,但依赖于从花括号初始化列表构造的隐式临时类型 _ab,即类似于:

    const _ab& i = _ab{1, 2};
    

    (不同之处在于,如果 _ab 的 initializer_list 构造函数是 explicit,则第一种形式将不起作用,而即使对于显式构造函数,第二种形式也将起作用......但标记初始化列表构造函数 explicit 是一个坏主意,应该避免,因为它使初始化对象变得困难和混乱)。

    这种语法不一定像上面写的那样有用,因为您通常只写const _ab{1, 2}; 而不是使用引用。但是,当您调用一个采用 const 引用的函数时,能够使用花括号初始化列表初始化参数很有用,例如采用std::pair&lt;int, const char*&gt; 的函数可以调用为func({1, "two"}),这比C++03 的等效函数func(std::make_pair(1, "two")) 更方便。为了使它起作用,引用的列表初始化必须有可能导致隐式创建临时对象,这会导致您询问的 const _ab&amp; = {1, 2} 表单。

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2013-04-03
      • 2020-11-29
      • 1970-01-01
      相关资源
      最近更新 更多