我试图理解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');
OR
String mystring = 'x';
在这两种情况下,我都会进入int部分。除非我指定值的类型,否则它默认为int。即使我使用参数更具体,例如将一个声明为int,另一个声明为double,而不是使用显式构造函数名称并以此方式调用
String mystring(2.5);
或者这样
String mystring = 2.5;
它总是默认为带有double参数的构造函数。所以,我很难理解显式的真实用法。你能给我一个例子吗?不使用显式将是一个真正的失败?
答案 0 :(得分:7)
explicit
旨在阻止隐式转换。只要您使用String(foo);
之类的内容,就可以进行显式转换,因此使用explicit
无法改变是成功还是失败。
因此,让我们看一下涉及隐式转换的场景。让我们从你的String
课程开始:
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")
- 您必须使用{{1} }}
但请注意,f(String("this is a string"));
仅控制从某种类型explicit
到您定义的类型的隐式转换。它对从foo
构造函数所采用的类型到的隐式转换没有影响。因此,采用类型explicit
的显式构造函数仍将采用浮点参数:
int
...因为这涉及从f(String(1.2))
到double
的隐式转换,然后是从int
到int
的显式转换。如果您要禁止从String
转换为double
,请执行此操作(例如)提供一个带String
的重载构造函数,但随后抛出:< / p>
double
现在从String(double) { throw("Conversion from double not allowed"); }
到double
的隐式转换不会发生 - int
将直接传递给您的ctor而不进行转换。
关于使用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
键入-
,那么会发生什么?
那些事情变得丑陋 - 代码仍在编译中,并且&#34;工作&#34; (对于这个词的某些定义),但打印出废话。在我的机器上进行快速测试时,我得到+
,但不要指望重复该特定结果。
此处的问题源于允许从-24
到String
的隐式转换。当我们尝试减去两个char *
对象时,编译器会试图找出我们的意思。由于它无法直接减去它们,因此它会查看是否可以将它们转换为某种支持减法的类型 - 果然,String
支持减法,因此它会转换我们的char const *
个对象到String
,然后减去两个指针。
如果我们将转化标记为char const *
:
explicit
...试图减去两个explicit operator char const *() { return data.c_str(); }
对象的代码只是简单地编译了。
同样的基本思想可以/确实适用于String
构造函数,但是演示它的代码变得更长,因为我们通常至少需要涉及几个不同的类。