什么是C++中的转换构造函数?它是为了什么?

kir*_*off 43 c++ constructor copy-constructor

我听说C++有一些叫做"转换构造函数"或"转换构造函数"的东西.这些是什么,它们的用途是什么?我在这段代码中看到了它:

class MyClass
{
  public:
     int a, b;
     MyClass( int i ) {}
}

 int main()
{
    MyClass M = 1 ;
}
Run Code Online (Sandbox Code Playgroud)

Jos*_*eld 57

转换构造函数的定义在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
};
Run Code Online (Sandbox Code Playgroud)

构造函数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};
}
Run Code Online (Sandbox Code Playgroud)

将返回值指定为braced-init-list的能力被视为转换.这使用转换构造函数为fooa float和a int.另外,我们可以通过这样做来调用这个功能bar({2.5f, 10}).这也是一种转换.由于它们是转换,因此它们用于转换构造函数的构造函数是有意义的.

因此,重要的是要注意,使构造函数foo采用a float和a int具有explicit函数说明符将阻止上述代码编译.只有在有可用于执行作业的转换构造函数时,才能使用上述新语法.

  • C++ 11:§6.6.3:

    return与语句支撑-INIT列表初始化对象或参考从功能通过从指定的初始化列表复制列表初始化(8.5.4)被返回.

    第8.5节:

    在参数传递[...]中发生的初始化称为复制初始化.

    §12.3.1:

    显式构造函数与非显式构造函数一样构造对象,但仅在显式使用直接初始化语法(8.5)或强制转换(5.2.9,5.4)的情况下才这样做.


kir*_*off 15

使用转换构造函数隐式转换

让我们在问题中做出更复杂的例子

class MyClass
{
  public:
     int a, b;
     MyClass( int i ) {}
     MyClass( const char* n, int k = 0 ) {}
     MyClass( MyClass& obj ) {}
}
Run Code Online (Sandbox Code Playgroud)

前两个构造函数正在转换构造函数.第三个是复制构造函数,因此它是另一个转换构造函数.

转换构造函数允许从参数类型到构造函数类型的隐式转换.这里,第一个构造函数可以实现从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") ;
}
Run Code Online (Sandbox Code Playgroud)

显式关键字和构造函数

现在,如果我们使用了explicit关键字怎么办?

class MyClass
{
  public:
     int a, b;
     explicit MyClass( int i ) {}
}
Run Code Online (Sandbox Code Playgroud)

然后,编译器不会接受

   int main()
    {
        MyClass M = 1 ;
    }
Run Code Online (Sandbox Code Playgroud)

因为这是隐式转换.相反,必须写

   int main()
    {
        MyClass M(1) ;
        MyClass M = MyClass(1) ;
        MyClass* M = new MyClass(1) ;
        MyClass M = (MyClass)1;
        MyClass M = static_cast<MyClass>(1);
    }
Run Code Online (Sandbox Code Playgroud)

explicit 始终使用关键字来防止构造函数的隐式转换,它适用于类声明中的构造函数.

  • 我也不认为构造函数需要有一个参数作为*转换构造函数*.它只需要非显式:"没有*function-specifier*`explicit`声明的构造函数指定从其参数类型到其类的类型的转换.这样的构造函数称为*转换构造函数*. " (2认同)