C++ 参考初始化表单

C++ 参考初始化表单,c++,c++11,gcc,language-lawyer,c++14,C++,C++11,Gcc,Language Lawyer,C++14,所以我测试了一些参考初始化表单,如下所述。不知何时: T & ref = { arg1, arg2, ... }; 及 表单将永远被使用,具体用途是什么。我想它是用来用“initializer_list”初始化临时数组和构造函数的,如下所示: int main() { struct _ab { _ab() {cout << "_ab()" << endl;} _ab(initializer_list<i

所以我测试了一些参考初始化表单,如下所述。不知何时:

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;
}
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;
    }
intmain()
{
结构
{
_ab(){cout
const\u ab&i();

上面的代码声明(并调用)一个函数,该函数返回对_abstruct的常量引用-这是第一个错误。第二个错误是您试图访问函数的成员变量(
i
),这是不可能的

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

const _ab & i {}; // probably will work fine
但是,忽略大括号将导致错误-此大括号不起作用:

const _ab & i;  // error
这也应该很好:

const _ab & i = {};
编辑:

只有当“匿名”对象是
const
引用时,才可以使用“匿名”对象初始化引用(就像在所有示例中一样)。这在语言中是允许的,我不确定是否有更深层的原因以及为什么不允许。因此,您实际上不使用初始值设定项列表初始化引用(或其他)-这些用于初始化匿名对象,此匿名对象用作常量引用的“目标”。如果要将构造函数声明为显式的,则需要指定类型,因此在这种情况下,可能不会混淆实际使用的初始化器:

const _ab & i = _ab{/*...*/};
这相当于:

const _ab anonymousObject = _ab{/*...*/};
const _ab & i = anonymousObject;
标准在[dcl.init]/8中的注释中对此进行了解释:

[注意:由于初始化器的语法不允许使用
()

X a();
不是类
X
对象的声明,而是声明 函数不带参数并返回一个
X

这适用于任何类型,被称为最麻烦的解析。当然,
i.a
因此格式不正确


但还是有人能解释一下为什么有这种可能性 要用初始化列表初始化,它的用途是什么

[dcl.init]/3:

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

  • [……]

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

  • 否则,如果
    T
    是引用类型,则T引用的类型的prvalue临时值将被复制列表初始化或初始化 已初始化直接列表,具体取决于的初始化类型 引用,并且引用绑定到该临时对象。
    [注意:通常,如果 引用类型是对非常量类型的左值引用。-结束注释]

最后一点是指以下事实:

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
结构{
S(标准::初始值设定项列表);//1
S(const std::string&);/#2
// ...
};
常量S&r1={1,2,3.0};//确定:调用#1
const S&r2{“Spinach”};//确定:调用#2
S&r3={1,2,3};//错误:初始值设定项不是左值
const int&i1={1};//确定
const int&i2={1.1};//错误:正在缩小
const int(&iar)[2]={1,2};//确定:iar绑定到临时数组
这对于函数调用也特别有用

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

f( {first, last} );
void f(std::vector const&);
f({first,last});

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

std::string str() { return "blah"; }

const std::string& s = str();
函数返回的prvalue绑定到引用,其生存期将延长,直到引用的生存期结束

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

const std::string& s = std::string();
这将创建一个绑定到引用的临时文件(将其生存期延长到引用的生存期)

const_ab&i={1,2}
的情况非常相似,但依赖于从带括号的init列表构造的类型为
\u ab
的隐式临时变量,即它类似于:

const _ab& i = _ab{1, 2};
(区别在于,如果
\u ab
的初始值设定项列表构造函数是
显式的
,则第一种形式不起作用,而第二种形式甚至对显式构造函数也起作用……但是标记初始值设定项列表构造函数
显式的
是一个坏主意,应该避免,因为这会使我感到困难和困惑。)初始化对象)


这种语法并不一定像上面所写的那样有用,因为您通常只编写
const\u ab{1,2};
而不是使用引用。但是,当您调用一个接受常量引用的函数时,能够使用带括号的init列表初始化参数是很有用的,例如,接受
std::pair
的函数可以被称为
func({1,“two”})
,这比C++03等效函数更方便:
func(std::make_pair(1,“two”)
。为了实现这一点,引用的列表初始化必须能够导致隐式创建一个临时变量,这将导致
常量ab&={1,2}
你要问的表单。

我已经编辑了我的答案。
\u ab
是一个奇怪的类型名称。
ab
有什么问题吗?所以实际上,初始化引用的初始化列表表单用于创建一个临时对象。
const std::string& s = std::string();
const _ab& i = _ab{1, 2};