gpt4 book ai didi

c++ - 为什么 std::bind() 在这种情况下不适用于常量?

转载 作者:行者123 更新时间:2023-11-30 02:42:12 25 4
gpt4 key购买 nike

让我先解释一下我试图完成的事情。我需要创建一个类型删除仿函数(使用模板和虚函数),它能够在我正在开发的 RTOS 的消息队列存储中“放置”一个新对象。这种“诡计”是必需的,因为我希望消息队列的大部分代码是非模板化的,只有真正需要类型信息的部分实现为此类类型删除仿函数。这是一个嵌入式微 Controller 的项目(*),所以请假设我不能用模板制作整个消息队列,因为在这样的环境中 ROM 空间不是无限的。

我已经有了可以将对象“复制构造”和“移动构造”到队列存储中(用于“推送”操作)的仿函数,而且我还有一个可以将对象“交换”出队列存储的仿函数存储(用于“弹出”操作)。要获得一个完整的集合,我需要一个能够将对象“放置”到队列存储中的仿函数。

下面是展示我在创建它时遇到的问题的最小示例。请注意,这是一个简化的场景,没有显示太多样板(没有类、没有继承等),但错误完全相同,因为根本原因可能也相同。另请注意,std::bind()(或使用动态分配的类似机制)的使用对我的用例至关重要。

#include <functional>

template<typename T, typename... Args>
void emplacer(Args&&... args)
{
T value {std::forward<Args>(args)...};
}

template<typename T, typename... Args>
void emplace(Args&&... args)
{
auto boundFunction = std::bind(emplacer<T, Args...>,
std::forward<Args>(args)...);
boundFunction();
}

int main()
{
int i = 42;
emplace<int>(i); // <---- works fine

emplace<int>(42); // <---- doesn't work...
}

在 PC 上使用 g++ -std=c++11 test.cpp 编译时,第一个实例(使用变量的实例)编译没有问题,但第二个实例(使用常量42 直接)抛出这个错误信息:

test.cpp: In instantiation of ‘void emplace(Args&& ...) [with T = int; Args = {int}]’:
test.cpp:21:17: required from here
test.cpp:13:16: error: no match for call to ‘(std::_Bind<void (*(int))(int&&)>) ()’
boundFunction();
^
In file included from test.cpp:1:0:
/usr/include/c++/4.9.2/functional:1248:11: note: candidates are:
class _Bind<_Functor(_Bound_args...)>
^
/usr/include/c++/4.9.2/functional:1319:2: note: template<class ... _Args, class _Result> _Result std::_Bind<_Functor(_Bound_args ...)>::operator()(_Args&& ...) [with _Args = {_Args ...}; _Result = _Result; _Functor = void (*)(int&&); _Bound_args = {int}]
operator()(_Args&&... __args)
^
/usr/include/c++/4.9.2/functional:1319:2: note: template argument deduction/substitution failed:
/usr/include/c++/4.9.2/functional:1315:37: error: cannot bind ‘int’ lvalue to ‘int&&’
= decltype( std::declval<_Functor>()(
^
/usr/include/c++/4.9.2/functional:1333:2: note: template<class ... _Args, class _Result> _Result std::_Bind<_Functor(_Bound_args ...)>::operator()(_Args&& ...) const [with _Args = {_Args ...}; _Result = _Result; _Functor = void (*)(int&&); _Bound_args = {int}]
operator()(_Args&&... __args) const
^
/usr/include/c++/4.9.2/functional:1333:2: note: template argument deduction/substitution failed:
/usr/include/c++/4.9.2/functional:1329:53: error: invalid initialization of reference of type ‘int&&’ from expression of type ‘const int’
typename add_const<_Functor>::type>::type>()(
^
/usr/include/c++/4.9.2/functional:1347:2: note: template<class ... _Args, class _Result> _Result std::_Bind<_Functor(_Bound_args ...)>::operator()(_Args&& ...) volatile [with _Args = {_Args ...}; _Result = _Result; _Functor = void (*)(int&&); _Bound_args = {int}]
operator()(_Args&&... __args) volatile
^
/usr/include/c++/4.9.2/functional:1347:2: note: template argument deduction/substitution failed:
/usr/include/c++/4.9.2/functional:1343:70: error: invalid initialization of reference of type ‘int&&’ from expression of type ‘volatile int’
typename add_volatile<_Functor>::type>::type>()(
^
/usr/include/c++/4.9.2/functional:1361:2: note: template<class ... _Args, class _Result> _Result std::_Bind<_Functor(_Bound_args ...)>::operator()(_Args&& ...) const volatile [with _Args = {_Args ...}; _Result = _Result; _Functor = void (*)(int&&); _Bound_args = {int}]
operator()(_Args&&... __args) const volatile
^
/usr/include/c++/4.9.2/functional:1361:2: note: template argument deduction/substitution failed:
/usr/include/c++/4.9.2/functional:1357:64: error: invalid initialization of reference of type ‘int&&’ from expression of type ‘const volatile int’
typename add_cv<_Functor>::type>::type>()(

我尝试在其他地方寻找灵感,但Intel的TBB库有类似的代码(concurent_queue)和类似的功能(有一个emplace函数)实际上是没有的完全放置 - 它立即构造对象并将其“移动”到队列中......

知道上面的代码有什么问题吗?我想这是非常小的事情,但我自己无法解决...


(*) - https://github.com/DISTORTEC/distortos

最佳答案

您已经解释了这到底是怎么回事 std::bind有效(它将一切都变成左值),并改用 lambda。然而,这并不是微不足道的。 Lambda 可以按值或按引用捕获。您有点需要两者的混合:右值引用应该假定为可能引用临时对象,因此应该按值捕获,并具有移动语义。 (注意:这确实意味着原始对象在 调用 lambda 之前被移动。)左值引用应该通过引用捕获,原因可能很明显。

完成这项工作的一种方法是手动将捕获的参数放入 tuple 中左值引用类型和非引用类型,并在你想调用函数时解压:

template <typename T>
struct remove_rvalue_reference {
typedef T type;
};

template <typename T>
struct remove_rvalue_reference<T &&> {
typedef T type;
};

template <typename T>
using remove_rvalue_reference_t = typename remove_rvalue_reference<T>::type;

template <typename F, typename...T, std::size_t...I>
decltype(auto) invoke_helper(F&&f, std::tuple<T...>&&t,
std::index_sequence<I...>) {
return std::forward<F>(f)(std::get<I>(std::move(t))...);
}

template <typename F, typename...T>
decltype(auto) invoke(F&&f, std::tuple<T...>&&t) {
return invoke_helper<F, T...>(std::forward<F>(f), std::move(t),
std::make_index_sequence<sizeof...(T)>());
}

template<typename T, typename... Args>
void emplacer(Args&&... args) {
T{std::forward<Args>(args)...};
}

template<typename T, typename...Args>
void emplace(Args&&...args)
{
auto boundFunction =
[args=std::tuple<remove_rvalue_reference_t<Args>...>{
std::forward<Args>(args)...}]() mutable {
invoke(emplacer<T, Args...>, std::move(args));
};
boundFunction();
}

调用emplace时带参数 T1 &, T2 && , args 将被捕获在 tuple<T1 &, T2> 中.当最终调用该函数时,元组将被解压(基本思想为 thanks to @Johannes Schaub - litb)。

lambda 需要是可变的,以允许在调用函数时移动捕获的元组。

这使用了几个 C++14 特性。大多数这些都可以避免,但如果不能在捕获列表中指定初始化程序,我不知道如何做到这一点:C++11 lambdas can only capture by reference (which would be reference to the local variable),或按值(value)(这将制作拷贝)。在 C++11 中,我认为这意味着唯一的方法是使用 lambda,而是有效地重新创建 std::bind 的大部分.

关于c++ - 为什么 std::bind() 在这种情况下不适用于常量?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/27330323/

25 4 0
Copyright 2021 - 2024 cfsdn All Rights Reserved 蜀ICP备2022000587号
广告合作:1813099741@qq.com 6ren.com