为什么没有默认构造函数就无法编译? [英] Why won't this compile without a default constructor?

查看:107
本文介绍了为什么没有默认构造函数就无法编译?的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我可以这样做:

#include <iostream>

int counter;

int main()
{
    struct Boo
    {
        Boo(int num)
        {
            ++counter;
            if (rand() % num < 7) Boo(8);
        }
    };

    Boo(8);

    return 0;
}

这样可以编译良好,我的反结果是 21 .但是,当我尝试创建通过构造函数参数而不是整数文字传递的 Boo 对象时,出现编译错误:

This will compile fine, my counter result is 21. However when I try to create the Boo object passing the constructor argument instead of an integer literal I get a compile error:

#include <iostream>

int counter;

int main()
{
    struct Boo
    {
        Boo(int num)
        {
            ++counter;
            if (rand() % num < 7) Boo(num); // No default constructor 
                                            // exists for Boo
        }
    };

    Boo(8);

    return 0;
}

在第二个示例中如何调用默认构造函数,而在第一个示例中如何不调用默认构造函数?这是我在Visual Studio 2017上遇到的错误.

How is a default constructor called in the second example but not in the first example? This is error I get on Visual Studio 2017.

在在线C ++编译器onlineGDB上,我得到以下错误:

On the online C++ compiler onlineGDB I get the errors:

error: no matching function for call to ‘main()::Boo::Boo()’
    if (rand() % num < 7) Boo(num);

                           ^
note:   candidate expects 1 argument, 0 provided

推荐答案

Clang给出以下警告消息:

Clang gives this warning message:

<source>:12:16: warning: parentheses were disambiguated as redundant parentheses around declaration of variable named 'num' [-Wvexing-parse]
            Boo(num); // No default constructor 
               ^~~~~

这是最令人头疼的解析问题.由于 Boo 是类类型的名称,而 num 不是类型名称,因此 Boo(num); 可以是a的构造. Boo 类型的临时变量,其中 num Boo 的构造函数的参数,也可以是声明 Boo num; 在声明符 num 周围带有多余的括号(声明符可能总是有).如果两者都是有效的解释,则该标准要求编译器假设一个声明.

This is a most-vexing parse issue. Because Boo is the name of a class type and num is not a type name, Boo(num); could be either the construction of a temporary of type Boo with num being argument to Boo's constructor or it could be a declaration Boo num; with extra parentheses around the declarator num (which declarators may always have). If both are valid interpretations, the standard requires the compiler to assume a declaration.

如果将其解析为声明,则 Boo num; 将调用默认构造函数(不带参数的构造函数),该构造函数不会由您或隐式声明(因为您声明了另一个构造函数)).因此该程序格式不正确.

If it is parsed as declaration, then Boo num; would call the default constructor (the constructor without arguments), which isn't declared either by you or implicitly (because you declared a another constructor). Therefore the program is ill-formed.

这不是 Boo(8); 的问题,因为 8 不能是变量的标识符(declarator-id),因此将其解析为调用创建一个以 8 作为构造函数参数的 Boo 临时对象,因此不会调用默认的构造函数(未声明),而是调用您手动定义的构造函数.

This is not an issue with Boo(8);, because 8 cannot be a variable's identifier (declarator-id), so it is parsed as a call creating a Boo temporary with 8 as argument to the constructor, thereby not calling the default constructor (which is not declared), but the one you defined manually.

您可以使用 Boo {num}; 而不是 Boo(num); (因为 {} 不允许在声明符周围),通过将临时变量命名为变量,例如 Boo temp(num); ,或将其作为操作数放入另一个表达式中,例如(Boo(num)); (void)Boo(num);

You can disambiguate this from a declaration by either using Boo{num}; instead of Boo(num); (because {} around the declarator is not allowed), by making the temporary a named variable, e.g. Boo temp(num);, or by putting it as an operand in another expression, e.g. (Boo(num));, (void)Boo(num);, etc.

请注意,如果默认构造函数可用,则声明将采用正确的格式,因为声明位于 if 的分支块作用域而不是函数的块作用域之内,并且仅会遮盖 num 在函数的参数列表中.

Note that the declaration would be well-formed if the default constructor was usable, because it is inside the if's branch block scope rather than the function's block scope and would simply shadow the num in the function's parameter list.

无论如何,将临时对象创建误用于应该是正常(成员)函数调用的东西似乎不是一个好主意.

In any case it doesn't seem a good idea to misuse temporary object creation for something that should be a normal (member) function call.

仅在意图中创建一个临时类型并立即将其丢弃,或者如果意图是创建一个直接用作临时对象的临时类型,则可能会发生这种特殊类型的最令人烦恼的分析,并在括号中使用单个非类型名称.初始化程序,例如 Boo boo(Boo(num)); (实际上声明了函数 boo ,该函数采用名为 num 且类型为 Boo 的参数并返回 Boo ).

This particular type of most-vexing parse with a single non-type name in the parenthesis can only happen because the intend is to create a temporary and immediately discard it or alternatively if the intend is to create a temporary used directly as an initializer, e.g. Boo boo(Boo(num)); (actually declares function boo taking a parameter named num with type Boo and returning Boo).

通常不打算立即丢弃临时变量,可以使用大括号初始化或双括号( Boo boo {Boo(num)} Boo boo(Boo {num}) Boo boo((Boo(num))); ,但不是 Boo boo(Boo((num))); ).

Discarding temporaries immediately is usually not intended and the initializer case can be avoided using brace-initialization or double-parantheses (Boo boo{Boo(num)}, Boo boo(Boo{num}) or Boo boo((Boo(num)));, but not Boo boo(Boo((num)));).

如果 Boo 不是类型名称,则它不能是声明,也不会出现问题.

If Boo wasn't a type name, it could not be a declaration and no problem occurs.

我还想强调指出,即使在类作用域和构造函数定义内部, Boo(8); 也会创建一个 Boo 类型的新临时变量.就像人们可能错误地认为的那样,它不是像通常的非静态成员函数那样,使用调用者的 this 指针对构造函数的调用.无法在构造函数主体内以这种方式调用另一个构造函数.这仅在构造函数的成员初始化器列表中是可能的.

I also want to emphasize that Boo(8); is creating a new temporary of type Boo, even inside the class scope and constructor definition. It is not, as one might erroneously think, a call to the constructor with the caller's this pointer like for usual non-static member functions. It is not possible to call another constructor in this way inside the constructor body. This is only possible in the member initializer list of the constructor.

即使由于缺少构造函数而导致声明不正确,也会发生这种情况,原因是 [stmt.ambig]/3 :

This happens even though the declaration would be ill-formed due to missing constructor, because of [stmt.ambig]/3:

消歧纯粹是句法上的;也就是说,在这样的陈述中出现的名称,除了它们是否是否输入类型名称,通常不会在其中使用或更改消除歧义.

The disambiguation is purely syntactic; that is, the meaning of the names occurring in such a statement, beyond whether they are type-names or not, is not generally used in or changed by the disambiguation.

[...]

歧义消除是在解析之前进行的,并且被声明为声明歧义的语句可能是格式错误的声明.

Disambiguation precedes parsing, and a statement disambiguated as a declaration may be an ill-formed declaration.


已在编辑中修复:我忽略了所涉及的声明与函数参数的作用域不同,因此如果构造函数可用,则声明的格式正确.无论如何,在进行歧义消除时都不会考虑这一点.还扩展了一些细节.


Fixed in edit: I overlooked the declaration in question being in a different scope than the function parameter and the declaration therefore being well-formed if the constructor was available. This is not considered during disambiguation in any case. Also expanded on some details.

这篇关于为什么没有默认构造函数就无法编译?的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
相关文章
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆