我试图了解 c++ 中显式关键字的用法,并在 SO 上查看了这个问题
What does the explicit keyword mean in C++?
但是,那里列出的示例(实际上都是前两个答案)对于用法并不是很清楚。例如,
// classes example
#include <iostream>
using namespace std;
class String {
public:
explicit String(int n); // allocate n bytes to the String object
String(const char *p); // initializes object with char *p
};
String::String(int n)
{
cout<<"Entered int section";
}
String::String(const char *p)
{
cout<<"Entered char section";
}
int main () {
String mystring('x');
return 0;
}
现在我已将 String 构造函数声明为显式,但是如果我不将其列为显式,如果我将构造函数称为,
String mystring('x');
或
String mystring = 'x';
在这两种情况下,我都会进入 int 部分。除非我指定值的类型,否则它默认为 int。即使我对参数更具体,比如将一个声明为 int,另一个声明为 double,并且不使用显式的构造函数名称并以这种方式调用它
String mystring(2.5);
或者这样
String mystring = 2.5;
它将始终默认为以 double 作为参数的构造函数。所以,我很难理解显式的真正用法。你能给我一个例子,不使用显式将是一个真正的失败吗?
最佳答案
explicit
旨在防止隐式转换。任何时候你使用类似 String(foo);
的东西,这是一个显式转换,所以使用 explicit
无论成功还是失败都不会改变。
因此,让我们看一个涉及隐式转换的场景。让我们从您的 String
开始类(class):
class String {
public:
explicit String(int n); // allocate n bytes to the String object
String(const char *p); // initializes object with char *p
};
然后让我们定义一个函数,它接收一个
String
类型的参数。 (也可以是 String const &
,但 String
暂时可以):int f(String);
您的构造函数允许从
char const *
进行隐式转换,但仅从 int
显式转换.这意味着如果我打电话:f("this is a string");
...编译器将生成代码以从字符串字面量构造一个 String 对象,然后调用
f
与此 String
对象。但是,如果您尝试调用:
f(2);
它会失败,因为
String
接受 int
的构造函数参数已标记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
将直接传递给您的 ctor,无需转换。至于用什么
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
构造函数,但是演示它的代码会变得更长,因为我们通常至少需要涉及几个不同的类。
关于c++ - 构造函数的显式关键字的使用,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/36310944/