这里有两个不同的问题,因为你对于“显式(explicit)”和“隐式(implicit)”的定义与标准定义不同(现有大部分答案都是基于标准定义,因为它们是在你添加包含自己定义的" 显式(explicit)"和" 隐式(implicit)"示例之前撰写的)。
好的,让我们首先考虑你所定义的" 显式(explicit)",你的定义是什么(我猜你称之为" 显式(explicit)"是因为你明确地写出了类型名称?):
integer int1 = integer(0, 100);
相对于你对隐式的定义:
integer int1(1, 100);
在这种情况下,第一个“explicit”调用与第二个“implicit”调用没有任何优势。但仍然有区别。第一个实际上使用两个参数的构造函数创建了一个临时对象,然后使用复制构造函数来创建int1。尽管在实践中编译器通常会优化掉这个额外的复制,但如果您的复制构造函数是私有的,它仍然不起作用,而第二个只需要两个参数的构造函数(您甚至可以将其视为缺点)。
但现在到实际的关于“explicit”和“implicit”的标准定义。显式构造函数调用是你明确调用的任何构造函数。实际上,每当您使用圆括号语法()创建对象时,您都会显式调用构造函数;否则就是隐式构造函数调用(也就是由编译器在幕后完成)。
integer int1;
integer int1(1, 100);
integer int1 = integer(0, 100);
void func(integer);
func(int1);
因此,只有默认构造函数和任何一个参数的构造函数(包括复制和移动构造函数)可以被隐式调用。在这方面特别的问题是,不是复制/移动构造函数的单参数构造函数:
struct integer
{
integer(int);
};
这允许编译器隐式地调用构造函数以转换类型,因此任何int
都可以隐式转换为integer
:
void func(integer);
func(42); // implicit call to int-constructor
为了禁止这种行为,您需要将构造函数标记为
explicit
:
struct integer
{
explicit integer(int);
};
explicit
关键字只允许显式调用构造函数(例如
func(integer(42))
),这样做的好处是它不会在后台引入未被注意或不必要的转换,从而导致各种难以发现的问题和重载解析的模糊性。因此,通常的做法是将任何转换构造函数(一个参数的非复制/移动构造函数)标记为
explicit
,并且很可能也是C++11最终引入
explicit
转换运算符的原因。
总之,根据您的定义和示例,使用
integer int1 = integer(1, 100);
而不是
integer int1(1, 100);
并没有任何优势,虽然它有一些(通常无关紧要)的差别。
但根据标准定义,
explicit
构造函数调用比
隐式构造函数调用有很多优势,因为实际上只有通过
显式构造函数调用才能明确地构造对象,而
隐式构造函数调用只在某些情况下在后台执行,并且仅适用于零个和一个参数的构造函数(正如
aschepler已经指出的那样)。显式标记转换构造函数为
explicit
具有防止在后台进行不需要的隐式转换的优点。
integer int1 = integer(0, 100)
和integer int1(1, 100)
之间选择确实没有意义。但请记住,显式构造函数调用的实际定义是完全不同的(而你的第二个示例实际上是显式构造函数调用),这使得大多数现有答案根本没有回答你的实际问题。并且显式构造函数调用具有许多优点,因为你大多数情况下使用它们来做一些隐式无法完成的事情。 - Christian Rau