为什么C++ 11不能将不可复制的函子移动到std :: function?

问题描述:

//------------------------------------------------------------------------------ 
struct A 
{ 
    A(){} 
    A(A&&){} 
    A& operator=(A&&){return *this;} 
    void operator()(){} 

private: 
    A(const A&); 
    A& operator=(const A&); 

    int x; 
}; 

//------------------------------------------------------------------------------ 
int main() 
{ 
    A a; 
    std::function<void()> func(std::move(a)); 
} 

“A :: A”:不能访问类“A”为什么C++ 11不能将不可复制的函子移动到std :: function?

声明为private成员好像当我引用或const捕捉的东西我可以做一个不可复制的拉姆达。但是,当我这样做时,它实际上可以用于std::function

+1

作为[解决方法](https://gist.github.com/vmilea/5815777),你可以在std :: function和你的函子之间放置一个可复制的适配器。该适配器有一个虚拟(移动副本)复制构造函数,并在复制时抛出。 – 2013-06-19 16:41:34

简短的回答是,C++ 11规范要求您的ACopyConstructiblestd::function一起使用。

这个要求的答案很长,因为std::function在构造函数中删除了你的函子的类型。要做到这一点,std::function必须通过虚拟功能访问您的仿函数的某些成员。这些包括调用操作符,拷贝构造函数和析构函数。由于这些是通过虚拟调用访问的,因此无论您是否真的使用std::function的拷贝构造函数,析构函数或调用操作符,都会“使用它们”。

+0

如果我错了(可能是我),请纠正我,但为了使用虚函数,您需要一个vtable,因此需要一个基类。在这种情况下哪个是基类? – akappa 2012-08-01 22:40:51

+5

请参阅http://*.com/questions/6324694/type-erasure-in-c-how-boostshared-ptr-and-boostfunction-work以了解类型删除的工作原理。基类是std :: function的实现细节。派生类也是一个实现细节,但也在提供的仿函数上模板化。 – 2012-08-01 23:12:18

+0

很有意思,谢谢。 – akappa 2012-08-01 23:17:40

这是Visual Studio中的一个错误。它试图隐藏一个副本(实际上它应该试图隐藏一个移动),这需要一个可访问的拷贝构造函数。最好的解决办法是简单地声明复制构造函数/赋值运算符,而不要定义它们。该类仍然是不可复制的,但是代码将会被编译,因为VS永远不会尝试实际调用复制构造函数。

+3

它显然确实尝试调用复制ctor,如果我按照你的建议,因为我得到一个未解决的外部因素。 – David 2012-08-01 22:09:55

+0

@Dave:是的,这就是为什么它是* bug *。该标准表示应该允许,但VS并没有做到这一点。 – 2012-08-01 22:11:31

+0

@NicolBolas这很好,但他的解决方法建议没有奏效。我不能这么说? (虽然DeadMG,我的确很欣赏答案) – David 2012-08-01 22:12:25