c – 有没有办法在不使用std :: move的情况下使用构造类初始化类?

// Example program
#include <iostream>
#include <string>
#include <utility>

class A {
public:
    int x; 
};

class B {
public:
    B(A &&a) : m_a(std::move(a)) {}
    A m_a;
};

int main()
{
    B var(std::move(A()));
    // B var(A()); // does not compile why?

    std::cout << var.m_a.x << "\n";

}

在上面的代码片段中,注释掉的行不会编译.出现错误消息,它将var视为函数声明.即使A具有构造函数的参数,它仍然被视为函数声明.有没有办法写它,所以它不会被视为函数声明?在这种情况下,使用typename没有帮助.

最佳答案
这个问题被称为the most vexing parse,它准确地描述了您的情况,解析器不知道您是否需要函数声明或对象的实例化.从某种意义上讲,它是模糊的,对于一个人来说,某些代码片段会做X,但对于编译器来说,它显然是在做Y.

绕过它的一种方法是使用list initialization语法:

B var{A()}; // note the use of brackets

这不再是模棱两可的,它会根据需要调用构造函数.您也可以在A或两者中使用它:

B var(A{});
B var{A{}};

现在,为什么它含糊不清?举例来说,函数参数的声明是函数的指针:

int foo(int (*bar)());

这里,参数是函数的指针类型,它不带参数,返回类型为int.声明函数指针的另一种方法是省略声明符中的括号:

int foo(int bar());

其中仍然声明一个与前一个函数相同的指针.由于我们处于声明参数(parameter-declaration-clause)的上下文中,所解析的语法是type-id,它部分建立在abstract-declarator之上.因此,这允许我们删除标识符:

int foo(int());

而且我们最终仍然使用相同的类型.

尽管如此,让我们检查一下你的代码并将它与上面的例子进行比较:

B var(A());

我们有一些看起来像B类型的变量声明用A()初始化.到现在为止还挺好.但是等等,你说这不编译!

The error message appears that it’s treating var like a function declaration.

var实际上是一个函数声明,即使对你来说它首先看起来并不像那样.此行为是由于[dcl.ambig.res]/1

[…] the resolution is to consider any construct that could possibly be a declaration a declaration.

这句话适用于此.回顾前面的例子:

int foo(int());

这和你的代码一样模棱两可:foo可能是一个声明,因此解决方案是将其解释为一个.你的B var(A())也可能是一个声明,所以它保持相同的分辨率.

该标准在[dcl.ambig.res]/1, example #1中有一些这些案例,并提供了一些关于如何在[dcl.ambig.res]/1, note #1消除歧义的提示:

[ Note: A declaration can be explicitly disambiguated by adding parentheses around the argument.
The ambiguity can be avoided by use of copy-initialization or list-initialization syntax, or by use of a non-function-style cast.
— end note
 ]

[ Example:

06007

— end example
 ]

转载注明原文:c – 有没有办法在不使用std :: move的情况下使用构造类初始化类? - 代码日志