【发布时间】:2023-03-14 22:37:01
【问题描述】:
对c++11统一初始化语法的天真、乐观和哦..这么错误的看法
我认为既然 C++11 用户定义类型对象应该使用新的 {...} 语法而不是旧的 (...) 语法构造(除了为 std::initializer_list 和类似参数重载的构造函数(例如 @987654325 @: size ctor vs 1 elem init_list ctor))。
好处是:没有狭窄的隐式转换,最麻烦的解析没有问题,一致性(?)。我认为没有问题,因为我认为它们是相同的(除了给出的示例)。
但他们不是。
纯粹的疯狂故事
{} 调用默认构造函数。
... 除非:
- 默认构造函数被删除并且
- 没有定义其他构造函数。
那么看起来是不是用value来初始化对象?...即使对象已经删除了默认构造函数,{}也可以创建对象。这难道不是违背了已删除构造函数的全部目的吗?
...除非:
- 该对象有一个已删除的默认构造函数,并且
- 定义了其他构造函数。
然后它以call to deleted constructor 失败。
...除非:
- 对象有一个已删除的构造函数,并且
- 没有定义其他构造函数并且
- 至少是一个非静态数据成员。
然后它会因缺少字段初始值设定项而失败。
但是你可以使用{value} 来构造对象。
好吧,也许这和第一个异常一样(值初始化对象)
...除非:
- 该类有一个已删除的构造函数
- 并且至少有一个数据成员在类内默认初始化。
那么{} 和{value} 都不能创建对象。
我确定我错过了一些。具有讽刺意味的是,它被称为uniform 初始化语法。我再说一遍:UNIFORM初始化语法。
这是什么疯子?
场景 A
已删除默认构造函数:
struct foo {
foo() = delete;
};
// All bellow OK (no errors, no warnings)
foo f = foo{};
foo f = {};
foo f{}; // will use only this from now on.
场景 B
删除默认构造函数,删除其他构造函数
struct foo {
foo() = delete;
foo(int) = delete;
};
foo f{}; // OK
场景 C
删除默认构造函数,定义其他构造函数
struct foo {
foo() = delete;
foo(int) {};
};
foo f{}; // error call to deleted constructor
场景 D
已删除默认构造函数,未定义其他构造函数,数据成员
struct foo {
int a;
foo() = delete;
};
foo f{}; // error use of deleted function foo::foo()
foo f{3}; // OK
场景 E
删除默认构造函数,删除T构造函数,T数据成员
struct foo {
int a;
foo() = delete;
foo(int) = delete;
};
foo f{}; // ERROR: missing initializer
foo f{3}; // OK
场景 F
删除默认构造函数,类内数据成员初始化器
struct foo {
int a = 3;
foo() = delete;
};
/* Fa */ foo f{}; // ERROR: use of deleted function `foo::foo()`
/* Fb */ foo f{3}; // ERROR: no matching function to call `foo::foo(init list)`
【问题讨论】:
-
这个stackoverflow.com/questions/23882409/… 回答了一半的问题。最重要的一个,但仍然没有回答类内数据成员初始化和非默认构造函数会发生什么。
-
对不起,我太仓促了。在这里,聚合初始化的执行正是因为构造函数被定义为已删除(在其第一次声明时)。
-
这不是现代 C++ 中疯狂的单一案例。多年来,我听说“C++ 很愚蠢,因为
static根据上下文的不同意味着非常不同的东西”(实际上只有两个非常不同的含义,并且在明显不同的上下文中)。然后decltype被发明了两个微妙不同的含义,具有非常微妙的不同用途:identifier与:(identifier) -
"没有狭义的隐式转换" 在一个特殊情况下禁止一个完全有效和有用的转换是件好事吗?
-
@curiousguy 我不明白你的意思
标签: c++ c++14 language-lawyer list-initialization aggregate-initialization