C++ 为什么可以';tc++;11将不可复制函子移动到std::函数? //------------------------------------------------------------------------------ 结构A { A(){} A(A&&{} A&运算符=(A&&){return*this;} void运算符()({} 私人: A(常数A&); A&运算符=(常数A&); int x; }; //------------------------------------------------------------------------------ int main() { A A; std::function func(std::move(a)); }

C++ 为什么可以';tc++;11将不可复制函子移动到std::函数? //------------------------------------------------------------------------------ 结构A { A(){} A(A&&{} A&运算符=(A&&){return*this;} void运算符()({} 私人: A(常数A&); A&运算符=(常数A&); int x; }; //------------------------------------------------------------------------------ int main() { A A; std::function func(std::move(a)); },c++,function,lambda,c++11,C++,Function,Lambda,C++11,“A::A”:无法访问类“A”中声明的私有成员 似乎当我通过引用或const捕获某个内容时,我可以生成一个不可复制的lambda。然而,当我这样做时,它实际上可以将它提供给std::function这是VisualStudio中的一个bug。它尝试省略一个副本(实际上它应该尝试省略一个移动),这需要一个可访问的副本构造函数。最好的解决方案是简单地声明复制构造函数/赋值运算符,而从不定义它们。该类仍然是不可复制的,但代码将被编译,因为VS永远不会尝试实际调用复制构造函数。简单的回答是,C++11规

“A::A”:无法访问类“A”中声明的私有成员


似乎当我通过引用或
const
捕获某个内容时,我可以生成一个不可复制的lambda。然而,当我这样做时,它实际上可以将它提供给
std::function

这是VisualStudio中的一个bug。它尝试省略一个副本(实际上它应该尝试省略一个移动),这需要一个可访问的副本构造函数。最好的解决方案是简单地声明复制构造函数/赋值运算符,而从不定义它们。该类仍然是不可复制的,但代码将被编译,因为VS永远不会尝试实际调用复制构造函数。

简单的回答是,C++11规范要求您的
A
CopyConstructible
std::function
一起使用


答案很长,因为
std::function
会删除构造函数中的函子类型,所以存在此要求。为此,
std::function
必须通过虚拟函数访问functor的某些成员。其中包括调用运算符、复制构造函数和析构函数。由于这些都是通过虚拟调用访问的,所以无论您是否实际使用了
std::function
的复制构造函数、析构函数或调用运算符,它们都会被“使用”。

如果我按照您的建议去做,它显然会尝试调用复制构造函数,因为我会得到一个未解析的外部命令。@Dave:是的,这就是为什么它是一个bug。标准说应该允许,但VS做得不对。@NicolBolas这很好,但他的变通建议不起作用。我不能这么说?(我很感激你的回答,不过DeadMG,谢谢)你提到的bug存在(而且非常讨厌),但这不是问题所在。如果我错了,请纠正我(可能我错了),但是,为了使用虚拟函数,你需要一个vtable,因此需要一个基类。在这种情况下,哪个是基类?有关类型擦除工作原理的说明,请参阅。基类是std::function的一个实现细节。派生类也是一个实现细节,但也是在提供的函子上模板化的。@HowardHinnant:你认为如果有人提出一个AMMENDENT来细化需求,使其成为每个操作的一部分,它会通过吗?(如果复制了
std::function
,则只要求
A
可复制构造。)@GManNickG:它能够通过的唯一机会是有一个附带的实现,该实现清楚地证明它可以通过所有现有的单元测试,并且拥有足够友好的版权许可证。我个人目前不知道如何创建这样的实现。但我喜欢这个行业的一点是,我总是在学习新的东西。:-)作为一个很好的C++14解决方案,您可以使用
fu2::unique_function
类,该类既是一个仅移动的类型,也允许将不可复制的可调用项移动到其中,典型的例子是lambda move在其上下文中捕获不可复制的对象。您可以在std::function和functor之间放置一个可复制的适配器。适配器有一个伪(复制时移动)复制构造函数,如果复制,则抛出。
//------------------------------------------------------------------------------
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));
}