我听说C ++有一些叫做“转换构造函数”或“转换构造函数”的东西。这些是什么,它们的用途是什么?我看到它提到了这段代码:
class MyClass
{
public:
int a, b;
MyClass( int i ) {}
}
int main()
{
MyClass M = 1 ;
}
答案 0 :(得分:50)
转换构造函数的定义在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
的情况下声明的构造函数指定从其第一个参数的类型到其类的类型的转换。这样的构造函数称为转换构造函数。
C ++ 11 :§12.3.1
在没有函数说明符
explicit
的情况下声明的构造函数指定从其参数类型到其类类型的转换。这样的构造函数称为转换构造函数。
为什么具有多个参数的构造函数被认为是在C ++ 11中转换构造函数?这是因为新标准为我们提供了一些方便的语法,用于使用 braced-init-lists 传递参数和返回值。请考虑以下示例:
foo bar(foo f)
{
return {1.0f, 5};
}
将返回值指定为 braced-init-list 的能力被视为转换。这使用foo
的转换构造函数,其中包含float
和int
。另外,我们可以通过bar({2.5f, 10})
调用此函数。这也是一种转换。由于它们是转换,因此它们使用的构造函数转换构造函数是有意义的。
因此,重要的是要注意,使foo
的构造函数float
和int
具有explicit
函数说明符将停止上述代码从编译。只有在有转换构造函数可用于执行此任务时,才能使用上述新语法。
C ++ 11 :§6.6.3:
带有 braced-init-list 的
return
语句通过copy-list-initialization(8.5.4)从指定的初始化程序初始化要从函数返回的对象或引用列表。
第8.5节:
在参数传递[...]中发生的初始化称为复制初始化。
§12.3.1:
显式构造函数与非显式构造函数一样构造对象,但仅在显式使用直接初始化语法(8.5)或强制转换(5.2.9,5.4)的情况下才这样做。
答案 1 :(得分:15)
使用转换构造函数隐式转换
让问题中的例子更复杂
class MyClass
{
public:
int a, b;
MyClass( int i ) {}
MyClass( const char* n, int k = 0 ) {}
MyClass( MyClass& obj ) {}
}
前两个构造函数正在转换构造函数。第三个是复制构造函数,因此它是另一个转换构造函数。
转换构造函数允许从参数类型到构造函数类型的隐式转换。这里,第一个构造函数允许从int
转换为类MyClass
的对象。第二个构造函数允许从字符串转换为类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
关键字始终用于防止构造函数的隐式转换,并且它适用于类声明中的构造函数。
答案 2 :(得分:1)
转换构造函数是单参数构造函数,在没有函数说明符explicit的情况下声明。编译器使用转换构造函数将对象从第一个参数的类型转换为转换构造函数的类的类型。