构造函数的显式关键字的用法 [英] usage of explicit keyword for constructors

查看:68
本文介绍了构造函数的显式关键字的用法的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我试图理解c ++中显式关键字的用法,并在SO
上查看了此问题显式关键字是什么意思在C ++中?

I was trying to understand the usage of explicit keyword in c++ and looked at this question on SO What does the explicit keyword mean in C++?

但是,此处列出的示例(实际上是前两个答案)在用法上都不太清楚。例如,

However, examples listed there (actually both top two answers) are not very clear regarding the usage. For example,

// 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构造函数声明为显式的,但可以说是否不列出它是显式的,如果我将构造函数称为

Now I have declared String constructor as explicit, but lets say if I dont list it as explicit, if I call the constructor as,

String mystring('x');

OR

String mystring = 'x';

在两种情况下,我都将进入int部分。直到并且除非我指定值的类型,否则它默认为int。即使我对参数更具体,例如将一个声明为int,将另一个声明为double,并且不对构造函数名称使用显式并以这种方式调用

In both cases, I will enter the int section. Until and unless I specify the type of value, it defaults to int. Even if i get more specific with arguments, like declare one as int, other as double, and not use explicit with constructor name and call it this way

String mystring(2.5);

或者通过这种方式

String mystring = 2.5;

它将始终默认为带有double作为参数的构造函数。因此,我很难理解显式的实际用法。您能否提供一个示例,其中不使用显式将是真正的失败?

It will always default to the constructor with double as argument. So, I am having hard time understanding the real usage of explicit. Can you provide me an example where not using explicit will be a real failure?

推荐答案

显式用于防止隐式转换。每当您使用诸如 String(foo); 之类的东西时,这都是显式转换,因此使用 explicit 不会改变

explicit is intended to prevent implicit conversions. Anytime you use something like String(foo);, that's an explicit conversion, so using explicit won't change whether it succeeds or fails.

因此,让我们看一个涉及隐式转换的场景。让我们从您的 String 类开始:

Therefore, let's look at a scenario that does involve implicit conversion. Let's start with your 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 ):

Then let's define a function that receives a parameter of type String (could also be String const &, but String will do for the moment):

int f(String);

您的构造函数允许从 char const * ,但只能从 int 进行显式转换。这意味着如果我打电话:

Your constructors allow implicit conversion from char const *, but only explicit conversion from int. This means if I call:

f("this is a string");

...编译器将生成代码以根据字符串文字构造一个String对象,然后用 String 对象调用 f

...the compiler will generate the code to construct a String object from the string literal, and then call f with that String object.

如果,但是,您尝试调用:

If, however, you attempt to call:

f(2);

它将失败,因为 String 构造函数带有 int 参数的标记为 explicit 。这意味着如果我想将 int 转换为 String ,则必须明确地做到这一点:

It will fail, because the String constructor that takes an int parameter has been marked explicit. That means if I want to convert an int to a String, I have to do it explicitly:

f(String(2));

如果 String(char const *); 构造函数也被标记为 explicit ,那么您将无法调用 f( this is a string)或者-您必须使用 f(String( this is a string));

If the String(char const *); constructor were also marked explicit, then you wouldn't be able to call f("this is a string") either--you'd have to use f(String("this is a string"));

但是请注意,显式仅控制从某些类型 foo 隐式转换为您定义的类型。它对其他类型的隐式转换没有影响,该类型由显式构造函数采用。因此,采用类型 int 的显式构造函数仍将采用浮点参数:

Note, however, that explicit only controls implicit conversion from some type foo to the type you defined. It has no effect on implicit conversion from some other type to the type your explicit constructor takes. So, your explicit constructor that takes type int will still take a floating point parameter:

f(String(1.2))

...因为涉及到隐式转换 double int ,然后从 int 显式转换为字符串。如果您想禁止从 double String 的转换,则可以(例如)提供一个重载的构造函数,它使用 double ,然后抛出:

...because that involves an implicit conversion from double to int followed by an explicit conversion from int to String. If you want to prohibit a conversion from double to String, you'd do it by (for example) providing an overloaded constructor that takes a double, but then throws:

String(double) { throw("Conversion from double not allowed"); }

现在从 double 隐式转换为 int 不会发生- double 将直接传递给您的ctor,而无需进行转换。

Now the implicit conversion from double to int won't happen--the double will be passed directly to your ctor without conversion.

关于使用 explicit 完成的工作:使用 explicit 的主要目的是防止编译的代码。当与重载结合使用时,隐式转换有时会导致一些相当奇怪的选择。

As to what using explicit accomplishes: the primary point of using explicit is to prevent code from compiling that would otherwise compile. When combined with overloading, implicit conversions can lead to some rather strange selections at times.

使用转换运算符而不是构造函数可以更容易地演示问题(因为您可以做到)只有一个班级)。例如,让我们考虑一个很小的字符串类,它与人们在不了解隐式转换有多大问题之前写的很多东西很相似:

It's easier to demonstrate a problem with conversion operators rather than constructors (because you can do it with only one class). For example, let's consider a tiny string class fairly similar to a lot that were written before people understood as much about how problematic implicit conversions can be:

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 来分配内存)。

(I've cheated by using std::string to store the data, but the same would be true if I did like they did and stored a char *, and used new to allocate the memory).

现在,这可以使此工作正常进行:

Now, this makes things like this work fine:

Foo a("a");
Foo b("b");

std::cout << a + b;

...,(当然)结果是它打印出 ab 。但是,如果用户犯了一个小错误,并在想要输入 + 的地方键入-,会发生什么?

...and, (of course) the result is that it prints out ab. But what happens if the user makes a minor mistake and types - where they intended to type +?

这就是丑陋的地方-代码仍然可以编译和运行(对于单词的某些定义),但打印出来毫无意义。在我的计算机上进行的快速测试中,我得到了 -24 ,但是不要指望复制该特定结果。

That's where things get ugly--the code still compiles and "works" (for some definition of the word), but prints out nonsense. In a quick test on my machine, I got -24, but don't count on duplicating that particular result.

这里的问题源于允许从 String 隐式转换为 char * 。当我们尝试减去两个 String 对象时,编译器试图弄清楚我们的意思。由于它不能直接将它们相减,因此会检查是否可以将它们转换为支持减法的类型-可以肯定的是, char const * 支持减法,因此它将两个 String 对象都转换为 char const * ,然后减去两个指针。

The problem here stems from allowing implicit conversion from String to char *. When we try to subtract the two String objects, the compiler tries to figure out what we meant. Since it can't subtract them directly, it looks at whether it can convert them to some type that supports subtraction--and sure enough, char const * supports subtraction, so it converts both our String objects to char const *, then subtracts the two pointers.

如果我们将转化标记为显式,则:

If we mark that conversion as explicit instead:

explicit operator char const *() { return data.c_str(); }

...试图减去两个 String 对象根本无法编译。

...the code that tries to subtract two String objects simply won't compile.

相同的基本思想可以/确实适用于 explicit 构造函数,但用于演示它的代码会更长,因为我们通常至少需要涉及几个不同的类。

The same basic idea can/does apply to explicit constructors, but the code to demonstrate it gets longer because we generally need at least a couple of different classes involved.

这篇关于构造函数的显式关键字的用法的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆