什么是C ++中的转换构造函数? 这是为了什么?
我听说C ++有一些名为“转换构造函数”或“转换构造函数”的东西。 这些是什么,它们是什么? 我看到它提到这个代码:
class MyClass { public: int a, b; MyClass( int i ) {} } int main() { MyClass M = 1 ; }
C ++ 03和C ++ 11之间的转换构造函数的定义是不同的。 在这两种情况下,它必须是一个非explicit
构造函数(否则它不会涉及隐式转换),但对于C ++ 03,它也必须可以用一个参数来调用。 那是:
struct foo { foo(int x); // 1 foo(char* s, int x = 0); // 2 foo(float f, int x); // 3 explicit foo(char x); // 4 };
构造函数1和2都是在C ++ 03和C ++ 11中转换构造函数。 构造函数3必须带有两个参数,它只是C ++ 11中的一个转换构造函数。 最后,构造函数4不是一个转换构造函数,因为它是explicit
。
-
C ++ 03 :§12.3.1
一个声明了没有函数说明符
explicit
构造函数可以用一个参数来调用,它指定了从第一个参数types到它的types的转换。 这样的构造函数被称为转换构造函数。 -
C ++ 11 :§12.3.1
没有
explicit
声明的构造函数指定了从其参数types到其类的types的转换。 这样的构造函数被称为转换构造函数。
为什么具有多个参数的构造函数被认为是在C ++ 11中转换构造函数? 这是因为新的标准为我们提供了一些方便的语法来传递参数和使用braced-init-lists返回值。 考虑下面的例子:
foo bar(foo f) { return {1.0f, 5}; }
将返回值指定为braced-init-list的能力被认为是一种转换。 这使用foo
的转换构造函数,它接受一个float
和一个int
。 另外,我们可以通过bar({2.5f, 10})
来调用这个函数。 这也是一个转换。 由于它们是转换,所以它们用于转换构造函数的构造函数是有意义的。
因此,需要注意的是,使得具有float
和int
的foo
的构造函数具有explicit
函数说明符将停止编译上述代码。 上面的新语法只有在有可用的转换构造函数时才能使用。
-
C ++ 11 :§6.6.3:
带有braced-init-list的
return
语句通过copy-list-initialization(8.5.4)从指定的初始值设定项列表中初始化要从函数返回的对象或引用。第8.5节:
在parameter passing中发生的初始化称为复制初始化。
§12.3.1:
一个显式构造函数就像非显式构造函数一样构造对象,但是只有在明确使用直接初始化语法(8.5)或者强制转换(5.2.9,5.4)的情况下才这样做。
用转换构造函数隐式转换
让我们来举一个更复杂的例子
class MyClass { public: int a, b; MyClass( int i ) {} MyClass( const char* n, int k = 0 ) {} MyClass( MyClass& obj ) {} }
前两个构造函数正在转换构造函数。 第三个是复制构造函数,因此它是另一个转换构造函数。
一个转换构造函数可以实现从参数types到构造函数types的隐式转换。 在这里,第一个构造函数可以从int
类转换为MyClass
类的对象。 第二个构造函数可以将string转换为MyClass
类的对象。 第三…从类MyClass
的对象到类MyClass
的对象!
要作为一个转换构造函数,构造函数必须有单个参数(在第二个参数中,第二个参数有一个默认值),并声明没有关键字explicit
。
那么,在main中的初始化可以像这样:
int main() { MyClass M = 1 ; // which is an alternative to MyClass M = MyClass(1) ; MyClass M = "super" ; // which is an alternative to MyClass M = MyClass("super", 0) ; // or MyClass M = MyClass("super") ; }
明确的关键字和构造函数
现在,如果我们使用了explicit
关键字呢?
class MyClass { public: int a, b; explicit MyClass( int i ) {} }
然后,编译器不会接受
int main() { MyClass M = 1 ; }
因为这是隐式转换。 相反,必须写
int main() { MyClass M(1) ; MyClass M = MyClass(1) ; MyClass* M = new MyClass(1) ; MyClass M = (MyClass)1; MyClass M = static_cast<MyClass>(1); }
explicit
关键字总是用于防止构造函数的隐式转换,并且它适用于类声明中的构造函数。