explicit
的作用是防止隐式转换。任何时候您使用类似于String(foo);
这样的内容,那都是一个显式转换,因此使用explicit
不会改变其成功或失败的情况。
因此,让我们看一个涉及隐式转换的场景。让我们从您的String
类开始:
class String {
public:
explicit String(int n);
String(const char *p);
};
那么我们定义一个函数,该函数接收一个 String
类型的参数(也可以是 String const &
,但目前 String
就足够了):
int f(String);
你的构造函数允许从char const *
进行隐式转换,但仅允许从int
进行显式转换。这意味着如果我调用:
f("this is a string");
编译器将生成代码,从字符串文字构造一个String对象,然后用该String对象调用f函数。
但如果您尝试调用:
f(2);
这将失败,因为接受一个int
参数的String
构造函数被标记为explicit
。这意味着如果我想将int
转换为String
,我必须明确地执行:
f(String(2));
如果
String(char const*)
构造函数也被标记为
explicit
,那么你将无法调用
f("this is a string")
,而需要使用
f(String("this is a string"));
。请注意,
explicit
仅控制从某种类型
foo
到您定义的类型的隐式转换,对于从其他类型
到您的
explicit
构造函数所接受的类型的隐式转换没有影响。因此,接受类型
int
的显式构造函数仍将接受浮点参数。
f(String(1.2))
因为这涉及到从double
到int
的隐式转换,然后是int
到String
的显式转换。如果您想禁止从double
到String
的转换,可以通过提供接受double
参数的重载构造函数并抛出异常来实现。
String(double) { throw("Conversion from double not allowed"); }
现在,从double
到int
的隐式转换将不会发生——double
将直接传递给您的构造函数而不进行转换。
至于使用explicit
实现的目标:使用explicit
的主要目的是防止本应该编译的代码被编译。当与重载结合使用时,隐式转换有时可能会导致一些相当奇怪的选择。
更容易演示转换运算符的问题而不是构造函数(因为可以仅使用一个类)。例如,让我们考虑一个非常类似于人们在理解隐式转换存在问题之前编写的字符串类:
class Foo {
std::string data;
public:
Foo(char const *s) : data(s) { }
Foo operator+(Foo const &other) { return (data + other.data).c_str(); }
operator char const *() { return data.c_str(); }
};
(我使用了std::string
来存储数据,这其实是在偷懒,但是如果像他们一样使用char *
来存储,并使用new
来分配内存,结果也是一样的。)
现在,这个方法可以很好地运行:
Foo a("a");
Foo b("b");
std::cout << a + b;
……而且,(当然)结果是输出ab
。但如果用户在打算键入+
时打成了-
会发生什么?
这时情况就变得丑陋了——代码仍然能够编译和“运行”(某种程度上),但输出的是无意义的内容。我在自己的机器上进行了一个快速测试,得到了-24
的结果,但不保证你能复现此结果。
问题出在允许从String
到char *
的隐式转换上。当我们尝试相减两个String
对象时,编译器试图弄清楚我们的意图。由于它不能直接相减它们,它检查是否可以将它们转换为支持减法的某种类型——果然,char const *
支持减法,因此它将我们的两个String
对象转换为char const *
,然后相减。
如果我们将该转换标记为explicit
:
explicit operator char const *() { return data.c_str(); }
如果试图减去两个String
对象的代码将无法编译。
相同的基本思想也适用于explicit
构造函数,但是为了演示它,我们通常需要至少涉及一对不同的类,因此代码要变得更长。
String('x')
调用String(const char*)
吗?'x'
是一个char
,而不是char
指针。无论如何,你只展示了String
的 显式 构造,所以explicit
没有任何区别。尝试调用一个带有int
的String
函数,它将无法工作,因为转换int
→String
不能 隐式 进行。 - BiffenString mystring = 'x';
不起作用。 - cpplearnerString
的函数,那么请参考 Jerry Coffin 的回答。 - Biffenexplicit
不会 失败,但可能会导致不期望的行为。 - Biffen