为什么赋值为std :: function <X()>在它是X类成员时不能编译?

Naw*_*waz 16 c++ lambda c++11 std-function

以下代码无法编译:

#include <functional>

struct X
{
    std::function<X()> _gen;
};

int main()
{
    X x;
    x._gen = [] { return X(); }; //this line is causing problem!
}
Run Code Online (Sandbox Code Playgroud)

我不明白为什么分配x._gen导致问题.无论GCC是给类似的错误消息.有人可以解释一下吗?


编译器错误消息

海湾合作委员会的错误:

In file included from main.cpp:1:0:
/usr/include/c++/4.8/functional: In instantiation of ‘std::function<_Res(_ArgTypes ...)>::_Requires<std::function<_Res(_ArgTypes ...)>::_CheckResult<std::function<_Res(_ArgTypes ...)>::_Invoke<_Functor>, _Res>, std::function<_Res(_ArgTypes ...)>&> std::function<_Res(_ArgTypes ...)>::operator=(_Functor&&) [with _Functor = main()::__lambda0; _Res = X; _ArgTypes = {}; std::function<_Res(_ArgTypes ...)>::_Requires<std::function<_Res(_ArgTypes ...)>::_CheckResult<std::function<_Res(_ArgTypes ...)>::_Invoke<_Functor>, _Res>, std::function<_Res(_ArgTypes ...)>&> = std::function<X()>&]’:
main.cpp:11:12:   required from here
/usr/include/c++/4.8/functional:2333:4: error: no matching function for call to ‘std::function<X()>::function(main()::__lambda0)’
    function(std::forward<_Functor>(__f)).swap(*this);
    ^
/usr/include/c++/4.8/functional:2333:4: note: candidates are:
/usr/include/c++/4.8/functional:2255:2: note: template<class _Functor, class> std::function<_Res(_ArgTypes ...)>::function(_Functor)
  function(_Functor);
  ^
/usr/include/c++/4.8/functional:2255:2: note:   template argument deduction/substitution failed:
/usr/include/c++/4.8/functional:2230:7: note: std::function<_Res(_ArgTypes ...)>::function(std::function<_Res(_ArgTypes ...)>&&) [with _Res = X; _ArgTypes = {}]
       function(function&& __x) : _Function_base()
       ^
/usr/include/c++/4.8/functional:2230:7: note:   no known conversion for argument 1 from ‘main()::__lambda0’ to ‘std::function<X()>&&’
/usr/include/c++/4.8/functional:2433:5: note: std::function<_Res(_ArgTypes ...)>::function(const std::function<_Res(_ArgTypes ...)>&) [with _Res = X; _ArgTypes = {}]
     function<_Res(_ArgTypes...)>::
     ^
/usr/include/c++/4.8/functional:2433:5: note:   no known conversion for argument 1 from ‘main()::__lambda0’ to ‘const std::function<X()>&’
/usr/include/c++/4.8/functional:2210:7: note: std::function<_Res(_ArgTypes ...)>::function(std::nullptr_t) [with _Res = X; _ArgTypes = {}; std::nullptr_t = std::nullptr_t]
       function(nullptr_t) noexcept
       ^
/usr/include/c++/4.8/functional:2210:7: note:   no known conversion for argument 1 from ‘main()::__lambda0’ to ‘std::nullptr_t’
/usr/include/c++/4.8/functional:2203:7: note: std::function<_Res(_ArgTypes ...)>::function() [with _Res = X; _ArgTypes = {}]
       function() noexcept
       ^
/usr/include/c++/4.8/functional:2203:7: note:   candidate expects 0 arguments, 1 provided
Run Code Online (Sandbox Code Playgroud)

同样,Clang抛出这个:

main.cpp:11:12: error: no viable overloaded '='
    x._gen = [] { return X(); };
    ~~~~~~ ^ ~~~~~~~~~~~~~~~~~~
/usr/lib/gcc/x86_64-linux-gnu/4.8/../../../../include/c++/4.8/functional:2270:7: note: candidate function not viable: no known conversion from '<lambda at main.cpp:11:14>' to 'const std::function<X ()>' for 1st argument
      operator=(const function& __x)
      ^
/usr/lib/gcc/x86_64-linux-gnu/4.8/../../../../include/c++/4.8/functional:2288:7: note: candidate function not viable: no known conversion from '<lambda at main.cpp:11:14>' to 'std::function<X ()>' for 1st argument
      operator=(function&& __x)
      ^
/usr/lib/gcc/x86_64-linux-gnu/4.8/../../../../include/c++/4.8/functional:2302:7: note: candidate function not viable: no known conversion from '<lambda at main.cpp:11:14>' to 'nullptr_t' for 1st argument
      operator=(nullptr_t)
      ^
/usr/lib/gcc/x86_64-linux-gnu/4.8/../../../../include/c++/4.8/functional:2192:39: note: candidate template ignored: disabled by 'enable_if' [with _Functor = <lambda at main.cpp:11:14>]
        using _Requires = typename enable_if<_Cond::value, _Tp>::type;
                                             ^
/usr/lib/gcc/x86_64-linux-gnu/4.8/../../../../include/c++/4.8/functional:2340:2: note: candidate template ignored: could not match 'reference_wrapper<type-parameter-0-0>' against '<lambda at main.cpp:11:14>'
        operator=(reference_wrapper<_Functor> __f) noexcept
        ^
Run Code Online (Sandbox Code Playgroud)

小智 8

这是PR60594,已在GCC 4.8.3中得到修复.对该bug的评论指出了它的有效性:尽管标准要求标准库模板的模板参数是完整类型(有一些例外),但X()即使X不是,也是完整类型.

有几个成员std::function<X()>隐含地要求X是一个完整的类型.你正在使用的模板构造函数就是其中之一:它要求你的lambda的返回类型可以隐式转换为X,但是是否X可以转换为自身取决于是否X是一个完整的类型:如果它是不完整的,编译器不能统治它是不可复制的不可移动类型的可能性.

该要求如下:

20.9.11.2.1 function construct/copy/destroy [func.wrap.func.con]

8 备注:除非fCallable(20.9.11.2)用于参数类型ArgTypes...和返回类型,否则这些构造函数不应参与重载决策R.

20.9.11.2类模板函数[func.wrap.func]

2一个可调用对象f类型的F可赎回为参数类型ArgTypes和返回类型R如果表达式,视为未计算的操作数(第5章),是公形成(20.9.2).INVOKE(f, declval<ArgTypes>()..., R)

20.9.2要求[func.require]

2定义为隐式转换为.INVOKE(f, t1, t2, ..., tN, R)INVOKE(f, t1, t2, ..., tN)R

其他几个成员std::function也需要X是一个完整的类型.

你只是类型X已经完成之后才使用那个构造函数,所以没有问题:在那一点上,X肯定可以隐式转换为X.

问题是在标准不支持执行此类检查的上下文中std::function执行依赖于X完整类型的检查,并且这没有考虑在已经完成X实例化之后将成为完整类型的可能性std::function<X()>.


Yak*_*ont 6

这可能是一个gcc bug,但也许不是.它不是直接=,而是在转换构造函数中std::function(operator=调用它).

这是一个病态的例子:

#include <iostream>
#include <functional>

struct X
{
  std::function<X()> _gen;
};

X func() {return {};};

int main()
{
  std::function<X()> foo1( &func ); // compiles
  X unused = X{}; // copy ctor invoked
  std::function<X()> foo2( &func ); // does not compile!
}
Run Code Online (Sandbox Code Playgroud)

请注意,第一个foo1工作正常,直到我导致某些代码调用副本ctor,第二个生成错误.即便auto unused =[]{ return X{}; };就够了.(func直接构造,永不复制).

复制ctor的使用/"创建"似乎导致了问题.

#include <iostream>
#include <functional>

struct X
{
  std::function<X()> _gen;
  X( X const& ) = default;
  X() = default;
};
X func() {return {};};

int main()
{
  std::function<X()> foo1( &func ); // does not compile
}
Run Code Online (Sandbox Code Playgroud)

复制构造函数最终调用复制ctor _gen,可能之前X是完整类型.

如果我们明确地延迟实例化X::X(X const&)直到X是一个完整的类型:

#include <functional>

struct X
{
  std::function<X()> _gen;
  X( X const& );
  X() {}
};
X::X( X const& o ):_gen(o._gen){} // or =default *here*

X func() {return {};};

int main()
{
  std::function<X()> foo1( &func ); // compiles!
  []{ return X{}; }; // or X unused = X{};
  std::function<X()> foo2( &func ); // compiles!
}
Run Code Online (Sandbox Code Playgroud)

问题消失了.

我怀疑XXwhen 的主体中创建的隐式复制构造函数X是一个不完整的类型隐式调用std::function<X()>的复制构造函数,它在一个X不完整的上下文中,这打破了其复制构造函数被调用的前提条件(至少在实践中)如何在gcc中实现 - 按标准实现?我不确定.)

通过明确地制作复制文件,X我避免这种情况,一切正常.

因此,作为解决问题的方法,X::X(X const&)在外面声明和实现X,魔术错误消失了.