MyClass a1 {a}; // clearer and less error-prone than the other three
MyClass a2 = {a};
MyClass a3 = a;
MyClass a4(a);
Run Code Online (Sandbox Code Playgroud)
为什么?
我在SO上找不到答案,所以让我回答一下我自己的问题.
这句话是什么?
return {};
Run Code Online (Sandbox Code Playgroud)
在C++ 11中指出,何时使用它而不是(比如说)
return NULL;
Run Code Online (Sandbox Code Playgroud)
要么
return nullptr;
Run Code Online (Sandbox Code Playgroud) 我认为,因为C++ 11用户定义的类型对象应该使用新{...}
语法而不是旧(...)
语法构造(除了构造函数重载std::initializer_list
和类似参数(例如std::vector
:size ctor vs 1 elem init_list ctor)).
好处是:没有狭义的隐式转换,最烦人的解析没有问题,一致性(?).我没有看到任何问题,因为我认为它们是相同的(除了给出的例子).
但他们不是.
该{}
调用默认的构造函数.
......除非:
然后它看起来像是值而不是初始化对象?...即使对象已经删除了默认构造函数,{}
也可以创建一个对象.这不是打败了删除的构造函数的全部目的吗?
......除非:
然后失败了call to deleted constructor
.
......除非:
然后失败并丢失了字段初始值设定项.
但是,您可以使用它{value}
来构造对象.
好吧也许这与第一个异常相同(值init对象)
......除非:
然后也{}
不能{value}
创建一个对象.
我相信我错过了一些.具有讽刺意味的是,它被称为统一初始化语法.我再说一遍:UNIFORM初始化语法.
这种疯狂是什么?
struct foo {
foo() = delete;
};
// All bellow OK (no errors, no warnings) …
Run Code Online (Sandbox Code Playgroud) c++ language-lawyer aggregate-initialization list-initialization c++14
就像标题所暗示的那样,我有一个简短的演示程序可以编译所有这些编译器,但是在使用gcc 4.8和gcc 4.9进行编译后运行的核心转储:
任何想法为什么?
#include <unordered_map>
struct Foo : std::unordered_map<int,int> {
using std::unordered_map<int, int>::unordered_map;
// ~Foo() = default; // adding this allows it to work
};
struct Bar {
Bar(Foo f = {}) : _f(std::move(f)) {}
// using any of the following constructors fixes the problem:
// Bar(Foo f = Foo()) : _f(std::move(f)) {}
// Bar(Foo f = {}) : _f(f) {}
Foo _f;
};
int main() {
Bar b;
// the following code works as expected
// Foo f1 …
Run Code Online (Sandbox Code Playgroud) 似乎普遍认为,大括号初始化应优先于其他形式的初始化,但是由于将C++ 17 扩展引入聚合初始化,似乎存在意外转换的风险.请考虑以下代码:
struct B { int i; };
struct D : B { char j; };
struct E : B { float k; };
void f( const D& d )
{
E e1 = d; // error C2440: 'initializing': cannot convert from 'D' to 'E'
E e2( d ); // error C2440: 'initializing': cannot convert from 'D' to 'E'
E e3{ d }; // OK in C++17 ???
}
struct F
{
F( D d ) …
Run Code Online (Sandbox Code Playgroud) 假设有一个std::array
要初始化的东西.如果使用双括号可以:
std::array<int, 2> x = {{0, 1}};
std::array<int, 2> x{{0, 1}};
Run Code Online (Sandbox Code Playgroud)
在良好的旧聚合初始化中使用单个括号也是可以的,因为括号省略将处理缺少的大括号:
std::array<int, 2> x = {0, 1};
Run Code Online (Sandbox Code Playgroud)
但是,使用单个括号的列表初始化是否可以?GCC接受它,Clang拒绝它"在使用直接列表初始化时不能省略关于子对象初始化的大括号".
std::array<int, 2> x{0, 1};
Run Code Online (Sandbox Code Playgroud)
提到括号内容的标准中唯一的部分是8.5.1/12,其中说:
使用赋值表达式初始化聚合成员时,将考虑所有隐式类型转换(第4节).如果赋值表达式可以初始化成员,则初始化成员.否则,如果成员本身是子集合,则假定使用大括号,并考虑赋值表达式初始化子集合的第一个成员.
8.5.1是关于聚合初始化的,所以应该意味着Clang是正确拒绝的,对吗?没那么快.8.5.4/3说:
列表初始化对象或类型T的引用定义如下:
[...]
- 否则,如果T是聚合,则执行聚合初始化(8.5.1).
我认为这意味着与聚合初始化完全相同的规则,包括括号elision,适用,意味着GCC是正确的接受.
我承认,措辞不是特别清楚.那么,哪个编译器正确处理第三个片段呢?括号省略是否在列表初始化中发生,或者不是?
c++ language-lawyer aggregate-initialization c++11 list-initialization
std::vector<char> p = {"abc", "def"};
Run Code Online (Sandbox Code Playgroud)
"abc"
并且"def"
不是char
,为什么编译器没有给我一个关于这种类型不匹配的错误?
我们知道这T v(x);
被称为直接初始化,而T v = x;
被称为复制初始化,这意味着它将构造一个临时T
的x
,将被复制/移入v
(很可能被省略).
对于列表初始化,标准根据上下文区分两种形式.T v{x};
称为直接列表初始化,T v = {x};
称为复制列表初始化:
§8.5.4 [dcl.init.list] p1
[...]列表初始化可以在直接初始化或复制初始化上下文中进行; 直接初始化上下文中的列表初始化称为直接列表初始化,复制初始化上下文中的列表初始化称为复制列表初始化.[...]
但是,整个标准中只有两个引用.对于直接列表初始化,在创建像T{x}
(§5.2.3/3
)这样的临时表时会提到它.对于copy-list-initialization,它用于返回语句中的表达式,如return {x};
(§6.6.3/2
).
现在,下面的片段怎么样?
#include <initializer_list>
struct X{
X(X const&) = delete; // no copy
X(X&&) = delete; // no move
X(std::initializer_list<int>){} // only list-init from 'int's
}; …
Run Code Online (Sandbox Code Playgroud) 当我运行此代码时:
struct X {
int a;
};
struct Y : public X {};
X x = {0};
Y Y = {0};
Run Code Online (Sandbox Code Playgroud)
我明白了:
error: could not convert ‘{0}’ from ‘<brace-enclosed initializer list>’ to ‘Y’
Run Code Online (Sandbox Code Playgroud)
为什么大括号初始化适用于基类而不适用于派生类?
请考虑以下代码段:
#include <iostream>
struct A {
A() {}
A(const A&) {}
};
struct B {
B(const A&) {}
};
void f(const A&) { std::cout << "A" << std::endl; }
void f(const B&) { std::cout << "B" << std::endl; }
int main() {
A a;
f( {a} ); // A
f( {{a}} ); // ambiguous
f( {{{a}}} ); // B
f({{{{a}}}}); // no matching function
}
Run Code Online (Sandbox Code Playgroud)
为什么每个调用都会编写相应的输出?支撑数如何影响均匀初始化?支撑精益如何影响这一切?
c++ language-lawyer uniform-initialization c++11 list-initialization