C++ 显式默认构造函数的用途
我最近注意到C++0x中有一个类调用显式默认构造函数。然而,我没有想到一个可以隐式调用默认构造函数的场景。它似乎是一个毫无意义的说明符。我想它可能会禁止c类代码>支持C++ 显式默认构造函数的用途,c++,default-constructor,explicit,explicit-constructor,C++,Default Constructor,Explicit,Explicit Constructor,我最近注意到C++0x中有一个类调用显式默认构造函数。然而,我没有想到一个可以隐式调用默认构造函数的场景。它似乎是一个毫无意义的说明符。我想它可能会禁止c类支持类别c=Class()但情况似乎并非如此 C++0x FCD中的一些相关引用,因为我更容易导航[类似的文本存在于C++03中,如果不在相同的位置] 12.3.1.3[类别转换] 默认构造函数可以是显式构造函数;此类构造函数将用于执行默认初始化或值初始化(8.5) 它接着提供了一个显式默认构造函数的示例,但它只是模仿了我上面提供的示例 8.
类别c=Class()代码>但情况似乎并非如此
C++0x FCD中的一些相关引用,因为我更容易导航[类似的文本存在于C++03中,如果不在相同的位置]
12.3.1.3[类别转换]
默认构造函数可以是显式构造函数;此类构造函数将用于执行默认初始化或值初始化(8.5)
它接着提供了一个显式默认构造函数的示例,但它只是模仿了我上面提供的示例
8.5.6[起始日期]
默认初始化T类型的对象意味着:
-如果T是(可能是cv限定的)类类型(第9条),则调用T的默认构造函数(如果T没有可访问的默认构造函数,则初始化是错误的)
8.5.7[起始日期]
初始化T类型对象的值意味着:
-如果T是一个(可能是cv限定的)类类型(第9条),具有用户提供的构造函数(12.1),则调用T的默认构造函数(如果T没有可访问的默认构造函数,则初始化是错误的)
在这两种情况下,标准都会调用默认构造函数。但如果默认构造函数是非显式的,就会发生这种情况。为了完整性起见:
8.5.11[起始日期]
如果没有为对象指定初始值设定项,则该对象默认为已初始化
据我所知,这只是没有数据的转换。这没有道理。我能想到的最好办法是:
void function(Class c);
int main() {
function(); //implicitly convert from no parameter to a single parameter
}
<>但是显然这不是C++处理默认参数的方式。还有什么东西可以使显式类()代码>的行为与Class()不同代码>
生成此问题的具体示例是std::function
[20.8.14.2 func.wrap.func]。它需要几个转换构造函数,其中没有一个标记为显式,但默认构造函数为。这声明了一个显式默认构造函数:
struct A {
explicit A(int a1 = 0);
};
A a = 0; /* not allowed */
A b; /* allowed */
A c(0); /* allowed */
如果没有参数,如以下示例中所示,explicit
是冗余的
struct A {
/* explicit is redundant. */
explicit A();
};
在一些C++0x草案中(我相信是n3035),它在以下方面起了作用:
A a = {}; /* error! */
A b{}; /* alright */
void function(A a);
void f() { function({}); /* error! */ }
但是在FCD中,它们(尽管,我怀疑它们没有考虑到这个特殊的原因)在这三种情况下都初始化各自的对象。值初始化不会进行重载解析,因此不会在显式构造函数上失败 除非另有明确说明,以下所有标准参考文件均指
(这个答案特别关注没有参数的显式默认构造函数)
案例#1[C++11到C++20]:空{}
非聚合的复制列表初始化禁止使用显式默认构造函数
受[强调我的]支配:
当初始化非聚合类类型T
的对象时
[dcl.init.list]指定执行重载解析
根据本节中的规则,重载解析选择
建造师分两个阶段:
- (1.1)最初,候选函数是类
T
和参数列表的初始值设定项列表构造函数([dcl.init.list])
由初始值设定项列表作为单个参数组成
- (1.2)如果未找到可行的初始值设定项列表构造函数,则再次执行重载解析,其中候选函数均为
类
T
的构造函数和参数列表由
初始值设定项列表的元素
如果初始值设定项列表没有元素且T
具有默认值
在构造函数中,省略了第一阶段In
复制列表初始化,如果选择了显式构造函数,则
初始化格式不正确。[ 注意:这与其他方法不同
情况([over.match.ctor],[over.match.copy]),其中
复制初始化时会考虑转换构造函数
仅当此初始化是最终初始化的一部分时,此限制才适用
过载解析的结果。 — 尾注 ]
对于非聚合,使用空括号init list进行复制列表初始化{}
禁止使用显式默认构造函数;例如:
struct Foo {
virtual void notAnAggregate() const {};
explicit Foo() {}
};
void foo(Foo) {}
int main() {
Foo f1{}; // OK: direct-list-initialization
// Error: converting to 'Foo' from initializer
// list would use explicit constructor 'Foo::Foo()'
Foo f2 = {};
foo({});
}
尽管上面的标准引用是指C++17,但这同样适用于C++11、C++14和C++20
案例#2[仅限C++17]:带有标记为explicit
的用户声明构造函数的类类型不是聚合
added在C++14和C++17之间进行了一些更新,主要是允许聚合从基类公开派生,但有一些限制,但也禁止聚合的显式构造函数[emphasismine]:
聚合是具有
- (1.1)未提供任何用户、
显式
或继承的构造函数([class.ctor])
- (1.2)无私有或受保护的非静态数据成员(第条[类访问])
- (1.3)无虚拟功能,以及
- (1.4)没有虚拟、私有或受保护的基类([class.mi])
从(禁止使用用户声明的构造函数进行聚合)开始,我们可能不再为聚合声明构造函数。但是,仅在C++17中,我们就有一个特殊的规则,即用户是否声明(但不是用户提供的)构造函数被标记为显式,以决定类类型是否为聚合。例如,类类型
struct Foo {
Foo() = default;
};
struct Bar {
explicit Bar() = default;
};
在C++11 thr中是聚合/不是聚合