天天看點

認識 C++ 中的 explicit 關鍵字

帶單一參數的構造函數在預設情況下隐含一個轉換操作符,請看下面的代碼:

class C {

int i;

//...

public:

      C(int i);//constructor and implicit conversion operator

//as well

};

void f() {

C c(0);

c = 5; //将 5 隐式轉換為 C 對象,然後指派

}

編譯器重新編輯上述例子代碼,如下:

//

//"c=5;" 被編譯器轉換成下面這個樣子:

/

C temp(5);// 執行個體化一個臨時對象,

c = temp; // 用 = 指派

temp.C::~C(); // temp 的析構函數被激活

在很多情況下,這個轉換是有意的,并且是正當的。但有時我們不希望進行這種自動的轉換,例如:

class String {

int size;

char *p;

//..

public:

       String (int sz); //這裡不希望進行隐式轉換操作

};

void f ()

{

    String s(10);

    // 下面是一個程式員的編碼;發生一個意想不到的轉換:

    s = 100; // 糟糕,100 被轉換為一個 String,然後被指派給 s

}

為了避免這樣的隐式轉換,應該象下面這樣顯式聲明該帶單一參數的構造函數:

class String {

int size;

char *p;

//..

public:

       // 不要隐式轉換

       explicit String (int sz);

       String (const char *s, int size n = 0); // 隐式轉換

};

void f ()

{

    String s(10);

    s = 100; // 現在編譯時出錯;需要顯式轉換:

    s = String(100); // 好;顯式轉換

    s = "st";        // 好;此時允許隐式轉換

}

這個 《ANSI/ISO C++ Professional Programmer's Handbook 》是這樣說的

explicit Constructors

A constructor that takes a single argument is, by default, an implicit conversion operator, which converts its argument to

an object of its class (see also Chapter 3, "Operator Overloading"). Examine the following concrete example:

class string

{

private:

int size;

int capacity;

char *buff;

public:

string();

string(int size); // constructor and implicit conversion operator

string(const char *); // constructor and implicit conversion operator

~string();

};

Class string has three constructors: a default constructor, a constructor that takes int, and a constructor that

constructs a string from const char *. The second constructor is used to create an empty string object with an

initial preallocated buffer at the specified size. However, in the case of class string, the automatic conversion is

dubious. Converting an int into a string object doesn't make sense, although this is exactly what this constructor does.

Consider the following:

int main()

{

string s = "hello"; //OK, convert a C-string into a string object

int ns = 0;

s = 1; // 1 oops, programmer intended to write ns = 1,

}

In the expression s= 1;, the programmer simply mistyped the name of the variable ns, typing s instead. Normally,

the compiler detects the incompatible types and issues an error message. However, before ruling it out, the compiler first

searches for a user-defined conversion that allows this expression; indeed, it finds the constructor that takes int.

Consequently, the compiler interprets the expression s= 1; as if the programmer had written

s = string(1);

You might encounter a similar problem when calling a function that takes a string argument. The following example

can either be a cryptic coding style or simply a programmer's typographical error. However, due to the implicit

conversion constructor of class string, it will pass unnoticed:

int f(string s);

int main()

{

f(1); // without a an explicit constructor,

//this call is expanded into: f ( string(1) );

//was that intentional or merely a programmer's typo?

}

'In order to avoid such implicit conversions, a constructor that takes one argument needs to be declared explicit:

class string

{

//...

public:

explicit string(int size); // block implicit conversion

string(const char *); //implicit conversion

~string();

};

An explicit constructor does not behave as an implicit conversion operator, which enables the compiler to catch the

typographical error this time:

int main()

{

string s = "hello"; //OK, convert a C-string into a string object

int ns = 0;

s = 1; // compile time error ; this time the compiler catches the typo

}

Why aren't all constructors automatically declared explicit? Under some conditions, the automatic type conversion is

useful and well behaved. A good example of this is the third constructor of string:

string(const char *);

The implicit type conversion of const char * to a string object enables its users to write the following:

string s;

s = "Hello";

The compiler implicitly transforms this into

string s;

//pseudo C++ code:

s = string ("Hello"); //create a temporary and assign it to s

On the other hand, if you declare this constructor explicit, you have to use explicit type conversion:

class string

{

//...

public:

explicit string(const char *);

};

int main()

{

string s;

s = string("Hello"); //explicit conversion now required

return 0;

}

Extensive amounts of legacy C++ code rely on the implicit conversion of constructors. The C++ Standardization

committee was aware of that. In order to not make existing code break, the implicit conversion was retained. However, a

new keyword, explicit, was introduced to the languageto enable the programmer to block the implicit conversion

when it is undesirable. As a rule, a constructor that can be invoked with a single argument needs to be declared

explicit. When the implicit type conversion is intentional and well behaved, the constructor can be used as an

implicit conversion operator.