在没有声明符的情况下显式调用构造函数

时间:2015-09-24 04:10:46

标签: c++

当我在阅读C ++中的most vexing parse时,我对表单a_type_name(args)的表达式的不同解释更加困惑(args是可选的)。至少有以下许多用例:

  1. 创建类型为a_type_name 的未命名临时用户。例如:

    Widget w = Widget("a simple widget");  // explicitly calling Widget constructor taking a string
    f = processWidget(Widget());  // pass a default constructed Widget to function processWidget
    

    我从来没有真正理解为什么这种表达方式是有效的,只是把它当作原样。毕竟,你可以做int()之类的事来定义(或声明?)一个未命名的整数吗?或double(3.14)初始化一个未命名的双?我无法在我的书 C ++ Primer 中找到这种表达式的任何正式定义--- Widget(),其中缺少声明符。任何人都能指出我的正式解释吗?

  2. 未命名的函数指针

    正如most vexing parse中所解释的那样,当Widget()可以声明一个没有命名的函数(指针?)时,什么都不带,并返回一个Widget。如果这是有效的,C ++类型如何推导出以下表达式:

    auto w = Widget(); // is w of type Widget Or a function pointer where the function takes no parameter and returns a Widget?
    

    有人可以列出Widget()表达式表示函数(指针)的所有上下文吗?

  3. C风格演员的功能形式

    如果存在转换路径,可以通过转换构造函数或转换运算符或其他一些预定义的隐式转换规则,a_type_name(one_arg)形式的表达式可能意味着显式类型转换:

    Widget("convert string to widget") // a form of explicit cast
    
  4. 在其他一些情况下,我可能还有其他解释。请帮我消除所有这些用例的歧义。

2 个答案:

答案 0 :(得分:1)

A a(10);

永远不会成为问题。它始终位于A类型的对象上,使用参数10构建。

A a(int);

永远不会成为问题。它始终是一个需要int并返回A的函数。

A a();

受到最令人烦恼的解析。

将其扩展到更复杂的东西。

A a(B b(), C c());

受到最令人烦恼的解析。

A a(B(10), c(30.2f));

不受最令人烦恼的解析。

A a(B b(int), C c(float));

不受最令人烦恼的解析。

答案 1 :(得分:0)

On point 1.假设Widget是结构或类,

f = processWidget( Widget() );

要使其工作,必须将processWidget声明为:

int processWidget( const & Widget );

虽然可能会因为Widget派生的类可以自动转换为Widget或其他提供此类转换的上下文这一事实而略显复杂,但我们只需要处理这个基本形式。 Widget()将“在现场”创建一个小部件。在其他情况下,我们可能已经写过了

Widget w();

这是完全相同的,除了现在创建的小部件有一个名称(w)。 Widget()将创建一个临时的,它将在它出现的语句完成后立即销毁。因此,此临时Widget作为const & Widget提供给函数processWidget。只要该函数执行,它就会持续。当该函数返回时,它将为f提供返回值,此时临时Widget将被销毁(将调用它的析构函数)。

请注意,您可以这样做:

Widget w;
w = Widget();

这个奇怪的用法构造了一个临时小部件,它被复制到w。这适用于您关于processWidget( Widget() )的问题的原因是此上下文中的=是运算符。它可能是一个重载的操作符函数,用于提供一些独特的复制操作,但即使是自动生成的它也需要一个参数,我在processWidget的声明中为参数提到了const & Widget。这意味着两者基本相同。

在这种程度上,int()做了类似的事情,但是这种确切的形式会在某些编译器中形成一个未初始化的int,这是不好的(在现代C ++中它将被初始化为零)。

但是,如果Widget()是一个返回值的函数,则声明为:

int Widget();

然后,必须以接受整数或转换为整数的类型的方式声明processWidget,以使调用起作用。

关于第2点:

如果有函数声明,

Widget()只会被“看作”为函数类型。如果Widget是结构或类,则编译器通过查找确定。当编译器试图理解Widget的使用意味着什么,然后是可识别的函数运算符()时,它然后“看到”有一个该名称的函数的声明,然后给出它知道返回类型,声明的参数等等。换句话说,Widget()作为表达式对C ++没有孤立的含义,不是关键字或其他可识别的声明,并且给出了上下文,意思是根据所提供的声明推断。如果Widget被声明为结构,编译器就不会“知道”Widget是一个函数声明,但可能会理解它是一个结构,为其提供(或将提供)自动默认构造函数,或者为哪一个给出了适当的匹配声明。

关于第3点。

构造函数被C ++理解为一个独特的函数类别。任何采用单个参数的构造函数都意味着所提供的参数是从其他给定类型构造对象的一种方式。在您的问题中,您提供的评论

Widget("convert string to widget") // a form of explicit cast

建议这是一个演员,但事实并非如此。这是转换。这是个很大的差异。强制转换不会创建新对象,但转换会创建。

假定采用一个参数的构造函数是转换声明,它们指定如何在给定其他类型的情况下创建Widget。如果你可以从const char *构建一个Widget,就像你暗示的那样,这意味着Widget类中的一个构造函数声明为:

Widget( const char * );

这意味着您可以从字符串文字中创建一个Widget,或者转换为字符串文字(const char *)。在可能需要Widget作为参数的上下文中,这告诉编译器如果提供了const char *,它可以构造一个。这与转换运算符完全相反,它们返回Widget的表示,就像它是其他类型一样。例如,如果您可以将Widget表示为std :: string,则可以为其提供转换运算符(但是,此类运算符可能很棘手)。一个相当典型的用法是为bool提供转换运算符。这并不意味着可以将Widget表示为bool,但是如果在bool的上下文中使用Widget类型,例如if ( !w ),其中w是Widget类型,则该Widget ACTS就像一个bool at那一刻,可能表明Widget不行,应该做点什么。