我听说 C++ 有一种叫做“转换构造函数”或“转换构造函数”的东西。这些是什么,它们是做什么用的?我看到它提到了这段代码:
class MyClass
{
public:
int a, b;
MyClass( int i ) {}
}
int main()
{
MyClass M = 1 ;
}
我听说 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
这样的构造函数称为转换构造函数。
C++11:§12.3.1
没有函数说明符声明的构造函数指定
explicit
从其参数的类型到其类的类型的转换。这样的构造函数称为转换构造函数。
为什么具有多个参数的构造函数被认为是 C++11 中的转换构造函数?这是因为新标准为我们提供了一些方便的语法,用于使用花括号初始化列表传递参数和返回值。考虑以下示例:
foo bar(foo f)
{
return {1.0f, 5};
}
将返回值指定为花括号初始化列表的能力被认为是一种转换。这使用了转换构造函数,foo
它需要 afloat
和 a int
。另外,我们可以通过doing调用这个函数bar({2.5f, 10})
。这也是一种转换。由于它们是转换,因此它们用来转换构造函数的构造函数是有意义的。
因此,重要的是要注意,使foo
接受 afloat
和 an的构造函数int
具有explicit
函数说明符会阻止上述代码编译。上述新语法只有在有可用于完成这项工作的转换构造函数时才能使用。
C++11:§6.6.3:
带有花括号初始化列表的
return
语句通过指定初始化列表中的复制列表初始化 (8.5.4) 初始化要从函数返回的对象或引用。
§8.5:
在参数传递 [...] 中发生的初始化 [...] 称为复制初始化。
§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 ) {}
}
前两个构造函数是转换构造函数。第三个是复制构造函数,因此它是另一个转换构造函数。
转换构造函数支持从参数类型到构造函数类型的隐式转换。在这里,第一个构造函数允许从 an 转换int
为 class 的对象MyClass
。第二个构造函数可以将字符串转换为 class 的对象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
关键字始终用于防止构造函数的隐式转换,它适用于类声明中的构造函数。
转换构造函数是一个单参数构造函数,在声明时不使用函数说明符 explicit 。编译器使用转换构造函数将对象从第一个参数的类型转换为转换构造函数的类的类型。