所以我正在测试一些参考初始化表单,描述为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;
}
在这种情况下,我试图用一个临时的&#39; _ab&#39;来初始化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:&#39; const main():: _ ab&amp; i()&#39;,使用本地类型&#39; const声明 main():: _ ab&#39;使用但从未定义[-fpermissive] |
错误2:请求会员&#39; a&#39; in&#39; i&#39;,这是非类型的 &#39; const main():: _ ab&amp;()&#39; |
你能告诉我上述2构造的确切含义吗?用于。
编辑:我理解第二个例子的问题。它声明了一个函数而不是变量。但仍然可以有人解释为什么引用有可能用初始化列表初始化并且它用于什么?
答案 0 :(得分:4)
const _ab & i();
该标准在[dcl.init] / 8的注释中解释了这一点:
[注意:由于初始化程序的语法不允许
()
,X a();
不是类
X
的对象的声明,而是声明 函数不带参数并返回X
。
无论何种类型都适用,并且被称为最令人烦恼的解析。当然i.a
因此是不正确的。
但仍有人可以解释为什么参考有可能 用初始化列表初始化它用于什么?
[dcl.init] / 3:
类型
T
的对象或引用的列表初始化定义如下:
[..]
否则,如果初始化列表具有类型
E
的单个元素,并且T不是引用类型或其引用类型是 与E
相关的引用,对象或引用从中初始化 那个元素;如果要求缩小转换(见下文) 将元素转换为T,程序格式不正确。否则,如果
T
是引用类型,则由T引用的类型的prvalue临时值是copy-list-initialized或 direct-list-initialized,取决于初始化的类型 引用,引用绑定到临时引用。
[注意:像往常一样,绑定将失败,如果程序结果不正确 reference类型是对非const类型的左值引用。 - 结束记录]
最后一个注释指的是
int& i = {1};
格式不正确,因为我们必须将1
绑定到非const左值引用,这是不可能的。
std::string& s = {20, '5'};
有效,因为我们必须使用临时初始化非const左值引用 然后标准给出了例子:
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} );
答案 1 :(得分:3)
const _ab & i();
上面的代码声明(和调用)一个函数,它返回对_ab结构的const引用 - 这是第一个错误。第二个错误是您尝试访问函数的成员变量(i
),这是不可能的。
要使用默认构造函数,您可以使用新的{...}
C ++ 11语法,它应该在您的示例中使用
const _ab & i {}; // probably will work fine
但是省略大括号会导致错误 - 这个不起作用:
const _ab & i; // error
这也应该可以正常工作:
const _ab & i = {};
编辑:
您可以使用&#34;匿名&#34;初始化参考。对象(就像在所有示例中一样)仅当它是const
引用时。在语言允许的情况下,我不确定是否有更深层次的理由以及为什么不允许这样做。因此,您实际上并没有使用初始化列表(或其他)初始化引用 - 这些用于初始化匿名对象,并且此匿名对象用作&#34; target&#34;用于const引用。如果您将构造函数声明为explicit
,则需要指定类型,因此在这种情况下,它可能不会像实际使用的初始化程序那样令人困惑:
const _ab & i = _ab{/*...*/};
这相当于:
const _ab anonymousObject = _ab{/*...*/};
const _ab & i = anonymousObject;
答案 2 :(得分:0)
您所看到的情况类似于允许将右值绑定到const引用的情况,例如
std::string str() { return "blah"; }
const std::string& s = str();
函数返回的prvalue绑定到引用,其生命周期延长,直到引用的生命周期结束。
另一种行使相同规则的方法是直接创建一个临时对象:
const std::string& s = std::string();
这会创建一个绑定到引用的临时(将其生命周期延长到引用的生命周期)。
const _ab& i = {1, 2}
的情况非常相似,但依赖于从braced-init-list构造的_ab
类型的隐式临时,即它类似于:
const _ab& i = _ab{1, 2};
(不同之处在于,如果_ab
的initializer_list构造函数为explicit
,则第一个表单不会起作用,而第二个表单甚至可以用于显式构造函数...但是标记初始化程序 - 列表构造函数explicit
是一个坏主意,应该避免使用,因为它会使初始化对象变得困难和混乱。)
这个语法并不一定与上面所写完全相同,因为您通常只需编写const _ab{1, 2};
而不是使用引用。但是当你调用一个带有const-reference的函数时,能够用braced-init-list初始化参数是有用的,例如:可以将std::pair<int, const char*>
的函数称为func({1, "two"})
,这比C ++ 03等效函数更方便:func(std::make_pair(1, "two"))
。为了使其起作用,引用的列表初始化必须能够导致隐式创建临时,这会导致您要求的const _ab& = {1, 2}
形式。