我需要将结构与已删除的复制构造函数绑定(bind)到一个函数。我已将我想要实现的目标简化为以下最小示例:
struct Bar {
số nguyên i;
Bar() = default;
Bar(Bar&&) = default;
Bar(const Bar&) = delete;
Bar& operator=(const Bar&) = delete;
};
void foo(Bar b) {
std::cout << b.i << std::endl;
}
int chính()
{
Bar b;
b.i = 10;
std::function a = std::bind(foo, std::move(b)); // ERROR
Một();
trả về 0;
}
从编译器中我得到的只有哀嚎和咬牙切齿:
test.cpp:22:27: error: no viable conversion from 'typename _Bind_helper<__is_socketlike::value, void (&)(Bar), Bar>::type' (aka '_Bind<__func_type (typename decay::type)>') to 'std::function'
std::function a = std::bind(foo, std::move(b));
^ ~~~~~~~~~~~~~~~~~~~~~~~~~~~~
/usr/bin/../lib/gcc/x86_64-linux-gnu/5.1.0/../../../../include/c++/5.1.0/functional:2013:7: note: candidate constructor not viable: no known conversion from 'typename _Bind_helper<__is_socketlike::value, void (&)(Bar),
Bar>::type' (aka '_Bind<__func_type (typename decay::type)>') to 'nullptr_t' for 1st argument
function(nullptr_t) noexcept
^
/usr/bin/../lib/gcc/x86_64-linux-gnu/5.1.0/../../../../include/c++/5.1.0/functional:2024:7: note: candidate constructor not viable: no known conversion from 'typename _Bind_helper<__is_socketlike::value, void (&)(Bar),
Bar>::type' (aka '_Bind<__func_type (typename decay::type)>') to 'const std::function &' for 1st argument
function(const function& __x);
^
/usr/bin/../lib/gcc/x86_64-linux-gnu/5.1.0/../../../../include/c++/5.1.0/functional:2033:7: note: candidate constructor not viable: no known conversion from 'typename _Bind_helper<__is_socketlike::value, void (&)(Bar),
Bar>::type' (aka '_Bind<__func_type (typename decay::type)>') to 'std::function &&' for 1st argument
function(function&& __x) : _Function_base()
^
/usr/bin/../lib/gcc/x86_64-linux-gnu/5.1.0/../../../../include/c++/5.1.0/functional:2058:2: note: candidate template ignored: substitution failure [with _Functor = std::_Bind]: no matching function for call to object of
type 'std::_Bind'
function(_Functor);
^
1 error generated.
所以我想问一下是否有任何解决方法可以让我将 Bar 绑定(bind)到 foo 同时保持 Bar 只移动。
biên tập:还要考虑以下代码,其中变量 b
的生命周期在 Một
被调用之前结束:
int chính()
{
std::function a;
{
Bar b;
b.i = 10;
a = std::bind(foo, std::move(b)); // ERROR
}
Một();
trả về 0;
}
std::hàm
不能采用仅移动可调用对象。它会删除传入的类型以调用(使用签名)、销毁和复制。1
编写一个只能移动的 std::hàm
只是一点工作。 Here is a stab at it在不同的背景下。 live example .
std::packaged_task
有趣的是也是一个只移动类型橡皮擦调用器,但它的重量比你可能想要的要重,而且要取出值是一件痛苦的事。
更简单的解决方案是滥用共享指针:
template
auto shared_function( F&& f ) {
auto pf = std::make_shared<>>(std::forward(f));
return [pf](auto&&... args){
return (*pf)(decltype(args)(args)...);
};
}
将一些可调用对象包装到共享指针中,将其放入 lambda 完美转发 lambda。
这说明了一个问题——调用不起作用!以上所有内容都有一个 hằng số
调用。
你想要的是一个你只能调用一次的任务。
template
struct task_once;
namespace details_task_once {
template
struct ipimpl;
template
struct ipimpl {
virtual ~ipimpl() {}
virtual R invoke(Args&&...args) && = 0;
};
template
struct pimpl;
template
struct pimpl:ipimpl {
F f;
template
pimpl(Fin&&fin):f(std::forward(fin)){}
R invoke(Args&&...args) && final override {
return std::forward(f)(std::forward(args)...);
};
};
// void case, we don't care about what f returns:
template
struct pimpl:ipimpl {
F f;
template
pimpl(Fin&&fin):f(std::forward(fin)){}
void invoke(Args&&...args) && final override {
std::forward(f)(std::forward(args)...);
};
};
}
template
struct task_once {
task_once(task_once&&)=default;
task_once&operator=(task_once&&)=default;
task_once()=default;
explicit operator bool() const { return static_cast(pimpl); }
R operator()(Args...args) && {
auto tmp = std::move(pimpl);
return std::move(*tmp).invoke(std::forward(args)...);
}
// if we can be called with the signature, use this:
template
class R2=R,
std::enable_if_t<
std::is_convertible<>,R2>{}
&& !std::is_same{}
>* = nullptr
>
task_once(F&& f):task_once(std::forward(f), std::is_convertible{}) {}
// the case where we are a void return type, we don't
// care what the return type of F is, just that we can call it:
template
class R2=R,
class=std::result_of_t,
std::enable_if_t<>{}>* = nullptr
>
task_once(F&& f):task_once(std::forward(f), std::is_convertible{}) {}
// this helps with overload resolution in some cases:
task_once( R(*pf)(Args...) ):task_once(pf, std::true_type{}) {}
// = nullptr support:
task_once( std::nullptr_t ):task_once() {}
riêng tư:
std::unique_ptr< details_task_once::ipimpl > pimpl;
// build a pimpl from F. All ctors get here, or to task() eventually:
template
task_once( F&& f, std::false_type /* needs a test? No! */ ):
pimpl( new details_task_once::pimpl>{ std::forward(f) } )
{}
// cast incoming to bool, if it works, construct, otherwise
// we should be empty:
// move-constructs, because we need to run-time dispatch between two ctors.
// if we pass the test, dispatch to task(?, false_type) (no test needed)
// if we fail the test, dispatch to task() (empty task).
template
task_once( F&& f, std::true_type /* needs a test? Yes! */ ):
task_once( f?task_once( std::forward(f), std::false_type{} ):task_once() )
{}
};
live example .
请注意,您只能在具有上述 task_once
的右值上下文中调用 ()
。这是因为 ()
是破坏性的,你的情况应该如此。
遗憾的是,以上依赖于 C++14。而且我现在不喜欢编写 C++11 代码。所以,这里有一个更简单但性能较低的 C++11 解决方案:
std::function a;
{
Bar b;
b.i = 10;
auto pb = std::make_shared(std::move(b));
a = [pb]{ return foo(std::move(*pb)); };
}
Một();
这会将 b
的移动拷贝推送到共享指针中,将其存储在 std::hàm
中,然后在您第一次调用 时破坏性地使用它()
.
1 没有它就实现move(除非它使用小函数优化,我希望它使用类型的move)。它还实现了转换回原始类型,但每种类型都支持这一点。对于某些类型,它支持 check-for-null(即,明确地转换为 bool),但老实说,我不确定它这样做的确切类型。
Tôi là một lập trình viên xuất sắc, rất giỏi!