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

时间:2016-12-23 04:02:08

标签: c++

// 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并没有帮助。

3 个答案:

答案 0 :(得分:9)

此问题称为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()进行初始化。到现在为止还挺好。但是等等,你说这不能编译!

  

出现错误消息,它将var视为函数声明。

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

引起的
  

[...]决议是考虑任何可能是声明声明的构造。

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

int foo(int());

这与你的代码一样模棱两可:foo可能是一个声明,因此解决方案是将其解释为一个。 <{1}}也可能是一个声明,因此它具有相同的分辨率。

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

  

[注意:通过在参数周围添加括号,可以明确消除声明的歧义。   可以通过使用复制初始化或列表初始化语法或使用非函数样式转换来避免歧义。    - 结束说明   ]

     

[实施例:

B var(A())
     

- 结束的例子   ]

答案 1 :(得分:2)

相反构建你的A

B var(A{});

并且不会混淆函数声明。

答案 2 :(得分:2)

B var = A();

如果B(A&&)构造函数不是explicit(在您的情况下,它不是,那么,这是一种有效的方法)。

B var(A{});

它也是解决问题的有效方法。

编译器认为你的原始行是一个函数声明,因为在C ++中,你可以在本地声明任何东西,甚至函数或结构:

int f()
{
   void f(); // You declare that function exists.
   class C; // You declare a class called `C` exists.
}

在你的情况下:

B var(A());

编译器看到它存在一个名为var的函数,返回B,并接收一个不接收任何参数并返回A的函数。

您必须使用上述两种方法中的任何一种消除歧义。

相关问题