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

时间:2013-11-21 18:56:10

标签: c++ c++11 lambda std-function

以下代码无法编译:

#include <functional>

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

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

我不明白为什么分配到x._gen导致问题。 gccclang都提供了类似的错误消息。有人可以解释一下吗?


编译器错误消息

GCC's error

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

同样地,Clang throws这个:

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
        ^

2 个答案:

答案 0 :(得分: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 备注:这些构造函数不应参与重载解析,除非f对于参数类型ArgTypes...是可调用的(20.9.11.2)并且返回类型R

     

20.9.11.2班级模板功能[func.wrap.func]

     

2类型f的可调用对象F对于参数类型ArgTypes Callable ,如果表达式,则返回类型R INVOKE (f, declval<ArgTypes>()..., R)被视为未评估的操作数(第5条),形成良好(20.9.2)。

     

20.9.2要求[func.require]

     

2将 INVOKE (f, t1, t2, ..., tN, R)定义为 INVOKE (f, t1, t2, ..., tN)隐式转换为R。< / p>

std::function的其他几位成员也需要X才能成为完整的类型。

在类型X已经完成之后,你只是使用那个构造函数,所以没有问题:那时,X当然可以隐式转换为X

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

答案 1 :(得分: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!
}

请注意,第一个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
}

复制构造函数最终调用_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!
}
问题消失了。

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

通过明确地在X之外制作副本ctor,我避免这种情况,一切正常。

因此,要解决您的问题,请在X::X(X const&)之外声明并实施X,然后神奇的错误就会消失。