我有一个未模仿的仿函数对象,我试图将其作为std::function
存储在另一个对象中。这个对象非常重量级,因此它被标记为不可复制,但它确实有一个移动构造函数。但是,尝试从临时构造函数构造std :: function或分配它失败。
以下是引发错误的最小示例。
// pretend this is a really heavyweight functor that can't be copied.
struct ExampleTest
{
int x;
int operator()(void) const {return x*2;}
ExampleTest( ) :x(0){}
ExampleTest( int a ) :x(a){}
// allow move
ExampleTest( ExampleTest &&other ) :x(other.x) {};
private: // disallow copy, assignment
ExampleTest( const ExampleTest &other );
void operator=( const ExampleTest &other );
};
// this sometimes stores really big functors and other times stores tiny lambdas.
struct ExampleContainer
{
ExampleContainer( int );
std::function<int(void)> funct;
};
/******** ERROR:
Compiler error: 'ExampleTest::ExampleTest' : cannot access private member
declared in class 'ExampleTest'
******************/
ExampleContainer::ExampleContainer( int x )
: funct( ExampleTest( x ) )
{}
/******** ERROR:
Compiler error: 'ExampleTest::ExampleTest' : cannot access private member
declared in class 'ExampleTest'
******************/
int SetExample( ExampleContainer *container )
{
container->funct = ExampleTest();
return container->funct();
}
在一个更简单的构造中,我只是制作一个本地函数,我也得到错误:
int ContrivedExample( )
{
// extra parens to sidestep most vexing parse
std::function<int()> zug( (ExampleTest()) );
/*** ERROR: 'ExampleTest::ExampleTest' : cannot access private member
declared in class 'ExampleTest' */
int troz = zug( ) ;
return troz;
}
据我所知,在所有这些情况下,临时的ExampleTest应该作为rvalue传递给函数构造函数。然而编译器想要复制它们。
是什么给出的?是否可以将不可复制(但可移动复制)的仿函数对象传递给std :: function构造函数?有指针等解决方法,但我想了解这里发生了什么。
上面的具体错误来自Visual Studio 2012和CTP C ++ 11补丁。 GCC 4.8和Clang 3也会出现故障,并带有错误消息。
答案 0 :(得分:18)
这个对象非常重量级,因此它被标记为不可复制,但它确实有一个移动构造函数。
如果仿函数是不可复制的,则它不符合与std::function
一起使用的必要条件。 C ++ 11标准的第20.8.11.2.1 / 7段规定:
template<class F> function(F f); template <class F, class A> function(allocator_arg_t, const A& a, F f);
7 需要:
F
应为CopyConstructible
。参数类型f
Callable
应为ArgTypes
(20.8.11.2) 并返回类型R
。A
的拷贝构造函数和析构函数不会抛出异常。
答案 1 :(得分:3)
std :: function可以从仿函数对象的rvalue构造。大多数实现都是这样做的。
std :: function的“我的目标必须是可复制构造的”要求是由于它自己的要求是可复制构造的。 std :: function的类型仅由其目标的签名(例如:void(int))定义,而std :: function本身由标准定义为可复制构造。所以当你复制构造一个std :: function时,它需要调用它的目标(底层函子)的copy-ctor。所以它需要它的目标。它没有其他选择。
要求目标是可复制构造的,当从rvalue可调用对象构造std :: function时,标准并没有说实现应该复制而不是移动。实现可能只会调用可调用对象的move-ctor。
有关示例和测试的更详细的附加信息:
例如,在任何可调用对象的std :: function的ctor的gcc(MSVC类似)实现中:
int main(){
using namespace std;
struct TFunctor
{
TFunctor() = default;
TFunctor(const TFunctor&) { cout << "cp ctor called" << endl; }
TFunctor(TFunctor&&) { cout << "mv ctor called" << endl; };
void operator()(){}
};
{ //!!!!COPY CTOR of TFunctor is NEVER called in this scope
TFunctor myFunctor;
//TFunctor move ctor called here
function<void()> myStdFuncTemp{ std::move(myFunctor) };
function<void()> myStdFunc{ move(myStdFuncTemp) };
}
{ //TFunctor copy ctor is called twice in this scope
TFunctor myFunctor;
//TFunctor copy ctor called once here
function<void()> myStdFuncTemp{ myFunctor };
//TFunctor copy ctor called once here
function<void()> myStdFunc{ myStdFuncTemp };
}
}
传递“_Functor __f”参数的值将使用其移动构造函数(如果有),如果它没有移动ctor,它将使用其复制构造函数。如下面的测试程序可以证明:
{{1}}
最后,您可以创建一个unstd :: function_only_movable,它几乎与std :: function完全相同但删除了自己的copy ctor,因此它不需要要求目标可调用对象拥有一个副本ctor。您还需要仅从可调用对象的右值构造它。