以下代码无法编译:
#include <functional>
struct X
{
std::function<X()> _gen;
};
int main()
{
X x;
x._gen = [] { return X(); }; //this line is causing problem!
}
我不明白为什么分配到x._gen
导致问题。 gcc和clang都提供了类似的错误消息。有人可以解释一下吗?
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
^
答案 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!
}
问题消失了。
我怀疑X
在X
正文中创建的X
隐式复制构造函数是一个不完整的类型隐式调用std::function<X()>
的复制构造函数,在X
不完整的上下文中,它打破了其复制构造函数被调用的前提条件(至少在实践中如何在gcc中实现 - 按标准?我不确定。)
通过明确地在X
之外制作副本ctor,我避免这种情况,一切正常。
因此,要解决您的问题,请在X::X(X const&)
之外声明并实施X
,然后神奇的错误就会消失。