什么是 C++ 中的转换构造函数?它是干什么用的?

What is a converting constructor in C++ ? What is it for?

提问人:kiriloff 提问时间:2/26/2013 最后编辑:Mooing Duckkiriloff 更新时间:1/12/2019 访问量:52310

问:

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

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

 int main()
{
    MyClass M = 1 ;
}
C++ 复制构造函数

评论

5赞 amdn 2/27/2015
危险!主机:C++03 和 C++11 标准的 §12.3.1 节描述了这种构造函数。参赛者:什么是转换构造函数?Stackoverflow 主机:错了,回答必须是真实问题的形式。

答:

24赞 kiriloff 2/26/2013 #1

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

让我们使问题中的示例更加复杂

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

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

转换构造函数支持从参数类型到构造函数类型的隐式转换。在这里,第一个构造函数允许从类的对象转换为对象。第二个构造函数允许从字符串转换为类的对象。第三...从类的对象到类的对象!intMyClassMyClassMyClassMyClass

要成为转换构造函数,构造函数必须具有单个参数(在第二个参数中,第二个参数具有一个默认值)并且声明时不带关键字。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关键字始终用于防止构造函数的隐式转换,它适用于类声明中的构造函数。

评论

0赞 Joseph Mansfield 2/26/2013
第一个示例中的第三个构造函数不是复制构造函数。复制构造函数参数必须是以下参数之一: 、 、 或 。X&const X&volatile X&const volatile X&
0赞 chris 2/26/2013
在最后一个示例中,您可以只写等。也要小心那些多字符文字。MyClass M(1);
0赞 Praetorian 2/26/2013
在发布代码之前,你有没有费心编译代码?字符串实际上是多字符常量,不会隐式转换为 !char const *
2赞 Joseph Mansfield 2/26/2013
我也不认为构造函数需要有一个参数才能成为转换构造函数。它只需要是非显式的:“一个没有函数说明符的声明的构造函数指定从其参数类型到其类类型的转换。这样的构造函数称为转换构造函数explicit
1赞 Joseph Mansfield 2/26/2013
@MooingDuck 它说“可以用单个参数调用”——基本上是一样的。
76赞 Joseph Mansfield 2/26/2013 #2

转换构造函数的定义在 C++03 和 C++11 之间是不同的。在这两种情况下,它都必须是非构造函数(否则它不会参与隐式转换),但对于 C++03,它也必须可以使用单个参数调用。那是:explicit

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 的能力被视为转换。这使用转换构造函数,因为它需要 a 和 an .此外,我们可以通过执行 .这也是一种转换。由于它们是转换,因此它们用于转换构造函数的构造函数是有意义的。foofloatintbar({2.5f, 10})

因此,需要注意的是,让 which 的构造函数 takes a 和 an have 函数说明符会阻止上述代码的编译。仅当有可用于执行该工作的转换构造函数时,才能使用上述新语法。foofloatintexplicit

  • C++ 11: §6.6.3:

    带有 braced-init-list 的语句通过 copy-list-initialization (8.5.4) 从指定的初始值设定项列表中初始化要从函数返回的对象或引用。return

    §8.5:

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

    §12.3.1:

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

4赞 Fazail awan 1/12/2019 #3

转换构造函数是在声明时不带函数说明符的单参数构造函数。编译器使用转换构造函数将对象从第一个参数的类型转换为转换构造函数的类的类型。