最近我写了一个非常简单的课.
class C
{
public:
void AddString(std::initializer_list<std::pair<const char*,int>> x)
{
//irrelevant
}
};
int main()
{
C c;
c.AddString({ {"1",1}, {"2", 2}, {"3", 3} });
.... //other unimportant stuff
return 0;
}
Run Code Online (Sandbox Code Playgroud)
令我惊喜的是,它编译并正常工作.有人可以向我解释一下编译器如何能够推断出嵌套的支撑初始化器是为了std::pair什么?我正在使用MSVS 2013.
c++ initializer-list c++11 list-initialization type-deduction
我最近读过一些关于使用大括号来允许多个用户定义转换的地方(不记得在哪里),但是构造函数转换和转换方法转换之间似乎存在差异,我不明白.
考虑:
#include <string>
using ::std::string;
struct C {
C() {}
};
struct A {
A(const string& s) {} // Make std::string convertible to A.
operator C() const { return C(); } // Makes A convertible to C.
};
struct B {
B() {}
B(const A& a) {} // Makes A convertible to B.
};
int main() {
B b;
C c;
// This works.
// Conversion chain (all thru ctors): char* -> string -> A -> B
b = …Run Code Online (Sandbox Code Playgroud) #include <iostream>
#include <string>
#include <typeinfo>
#include <typeindex>
#include <map>
#include <vector>
class Base{
public:
virtual ~Base() {}
};
class Derived: public Base { };
int main(){
int arr[10];
Derived d;
Base *p = &d;
std::map<std::type_index, std::string> proper_name = {
{typeid(int), "int"}, {typeid(double), "double"}, {typeid(float), "float"}, {typeid(char), "char"},
{typeid(Base), "Base"}, {typeid(Derived), "Derived"}, {typeid(std::string), "String"},
{typeid(int[10]), "Ten int Array"}, {typeid(p), "Base Pointer"}};
}
Run Code Online (Sandbox Code Playgroud)
我试图弄清楚这个列表初始化中发生的隐式转换.来自13.3.1.7N3337:
当非聚合类类型T的对象被列表初始化(8.5.4)时,重载决策分两个阶段选择构造函数:
最初,候选函数是类T的初始化列表构造函数(8.5.4),参数列表由初始化列表作为单个参数组成.
如果找不到可行的初始化列表构造函数,则再次执行重载解析,其中候选函数是类T的所有构造函数,参数列表由初始化列表的元素组成.
8.5.4:
构造函数是初始化列表构造函数,如果它的第一个参数是类型
std::initializer_list<E>或引用,可能std::initializer_list<E>是某些类型的cv-qualifiedE,并且没有其他参数或者所有其他参数都有默认参数
所以下面的构造函数列表用于 …
请考虑以下代码:
struct A {
int x;
};
int main() {
A a;
A b{a};
}
Run Code Online (Sandbox Code Playgroud)
这个程序是否符合C++ 11标准?在我的N3797副本中说
8.5.4列表初始化
[dcl.init.list]3:对象或类型引用的列表初始化
T定义如下:
- 如果T是聚合,则执行聚合初始化(8.5.1).
- 否则,如果T是std::initializer_list<E>,...的特化
- 否则,如果T是类类型,则考虑构造函数.枚举适用的构造函数,并使用重载决策选择最佳构造函数.如果转换任何类型需要缩小转换,则程序格式错误.
- 否则,如果初始化列表具有单个元素类型E且要么T不是引用类型,要么与引用相关E,则从该元素初始化对象或引用; 如果将元素转换为需要缩小转换T,则程序格式错误.
- 否则,如果T是引用类型,则类型引用的临时值prT是copy-list-initialized或direct-list-initialized,具体取决于引用的初始化类型,引用绑定到该临时.
- 否则,如果初始化列表没有元素,则对象进行值初始化.
- 否则,该程序格式不正确.
示例的要点是,类型是聚合,但列表初始化应该调用复制构造函数.在gcc 4.8和gcc 4.9,在C++ 11标准,它失败:
main.cpp: In function ‘int main()’:
main.cpp:7:8: error: cannot convert ‘A’ to ‘int’ in initialization
A …Run Code Online (Sandbox Code Playgroud) 我正在阅读这个答案,其中有以下示例:
struct R {};
struct S { S(R); };
struct T {
T(const T &); //1
T(S); //2
};
void f(T);
void g(R r) {
f({r});
}
Run Code Online (Sandbox Code Playgroud)
答案与[over.best.ics]/4的旧版本有关,当时看起来像这样:
但是,当考虑构造函数或用户定义的转换函数的参数时,[over.match.ctor]在类复制初始化的第二步中为复制/移动临时函数而调用时,通过[ over.match.list]当将初始化列表作为单个参数传递时,或者当初始化列表只有一个元素并且转换为某个类X或引用(可能是cv-qualified)时,X被认为是构造函数的第一个参数X的,或通过[over.match.copy],[over.match.conv]或[over.match.ref]在所有情况下,唯一的标准转换序列和省略号转换序列被考虑.
在答案中,据说没有上面引用中突出显示的部分,f({r})将是不明确的,因为它可以使用T(1)的第一个构造函数或第二个构造函数(2).
但是,尽管我尝试过,但我看不出第一个构造函数(1)是如何选择的.f({r})导致副本列表初始化的T距离{r}.如果使用第一个构造函数,则标准允许从r构造函数的参数类型转换.但是,只有一次转换是不够的,因为必须使用R - > S(使用转换构造函数S)然后S - > T(使用T(2)的转换构造函数).我在标准中找不到任何允许在列表初始化的转换中进行多个用户定义转换的内容.
我可能会遗漏一些东西.如果有人指出我错在哪里,或者我不是,我会很感激,我想知道标准引用中突出显示部分的目的是什么.
引用段落的当前版本要求初始化列表的唯一元素是初始化列表本身,这在上面的示例中是有意义的,如果不是这样的f({r})话f({{r}}).在这种情况下,解释是正确的.
谢谢.
我已经相信使用括号初始化时会为变量分配其默认值。但是我错了。
在以下示例中:
#include <string>
#include <iostream>
#include <stdint.h>
class A
{
public:
A() {}
~A(){}
int var1;
int32_t var2;
int64_t var3;
std::string var4;
double var5;
float var6;
std::string info() const {
return "var1=" + std::to_string(var1) + " " +
"var2=" + std::to_string(var2) + " " +
"var3=" + std::to_string(var3) + " " +
"var4=" + var4 + " " +
"var5=" + std::to_string(var5) + " " +
"var6=" + std::to_string(var6) + " " +
"\n"
;
}
};
int main() …Run Code Online (Sandbox Code Playgroud) 在下面的代码中,两次赋值有什么区别吗?在这两种情况下,value.v 是否会被默认构造,而 x 会被初始化为 42?
struct S
{
std::vector<int> v;
int x = 42;
};
S value;
void foo()
{
value = S();
value = { };
}
Run Code Online (Sandbox Code Playgroud) 几天前我发现了统一初始化,我看到每个人都应该尽可能地使用它.
但是,我不禁想到这种新语法比它的价值更麻烦......
第一个例子
假设我写了一个库,其中我有一个像这样的结构:
struct MyStruct
{
int member0;
int member1;
}
Run Code Online (Sandbox Code Playgroud)
用户可以使用聚合初始化来编写类似的东西:
MyStruct myVar = {0, 1}; // member0 = 0 and member1 = 1
Run Code Online (Sandbox Code Playgroud)
现在,让我们说我更新了我的库,结构现在看起来像这样:
struct MyStruct
{
int member0;
int member1;
MyStruct(int p0, int p1) : member0(p1), member1(p0){}
}
Run Code Online (Sandbox Code Playgroud)
在C++ 11之前,用户代码将停止编译,这会强制用户重写他的代码并使用构造函数.但是现在,代码将被编译并被解释为统一初始化:
MyStruct myVar = {0, 1}; // member0 = 1 and member1 = 0
Run Code Online (Sandbox Code Playgroud)
没有用户知道,更新他的库将使他的代码做一些非常不同的事情!
第二个例子
现在,让我们说我的库里有这样一个类:
class MyClass
{
public:
MyClass(int size, int default = 0) : elements(size, default){}
private:
std::vector<int> elements;
}
Run Code Online (Sandbox Code Playgroud)
用户可以像这样使用它:
MyClass myVar …Run Code Online (Sandbox Code Playgroud) 给定代码:
struct Test {
int a = 1;
int b = 2;
};
Test test1;
Test test2{};
Run Code Online (Sandbox Code Playgroud)
对于test2我敢肯定,这test2.a == 1和test2.b == 2.难道保证(或没有)相同的test1(不{})?
#include <array>
int main()
{
struct A
{
unsigned char l;
std::array<char, 12> c;
};
const A a = {1, "t"}; // OK
const A& ar = {1, "t"}; // error: invalid initialization of reference of type 'const main()::A&' from expression of type '<brace-enclosed initializer list>'
}
Run Code Online (Sandbox Code Playgroud)
(gcc 8.2,-std = c ++ 17)
这个问题谈到了一个海湾合作委员会的错误,但它已经过时了(7年前).
请注意,我不关心生命周期扩展,我实际上是将临时文件直接传递给函数而不是存储它,但我尝试将示例清理干净.
编辑:
array<char>.有效的东西是将字符串文字扩展为字符:
const A& ar = {1, {'a', 'b'}}; // works
Run Code Online (Sandbox Code Playgroud)