为什么 std::bind() 在这种情况下不适用于常量?
Why std::bind() doesn't work with constants in this scenario?
让我先解释一下我想要完成的目标。我需要创建一个类型擦除的函子(使用模板和虚拟函数(,该函子能够将新对象"放置"到我正在开发的 RTOS 的消息队列存储中。这种"欺骗"是必需的,因为我希望消息队列的大部分代码都是非模板化的,只有真正需要将类型信息实现为此类类型擦除函子的部分。这是一个嵌入式微控制器(*(的项目,所以请假设我只是不能用模板制作整个消息队列,因为在这样的环境中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...
}
当使用 g++ -std=c++11 test.cpp
在 PC 上编译时,第一个实例(使用变量的实例(编译没有问题,但第二个实例(直接使用常量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>()(
我尝试在其他地方寻找灵感,但英特尔的 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();
}
当用 args T1 &, T2 &&
调用 emplace
时,参数将被捕获在一个tuple<T1 &, T2>
中。元组在最终调用函数时被解压缩(感谢 @Johannes Schaub - 基本思想的 litb(。
lambda 必须是可变的,以允许在调用函数时从中移动捕获的元组。
这使用了多个 C++14 功能。其中大多数是可以避免的,但是如果没有能够在捕获列表中指定初始化器,我看不出如何做到这一点:C++11 lambda只能通过引用(这将是对局部变量的引用(或按值(这将创建副本(进行捕获。在 C++11 中,我认为这意味着唯一的方法不是使用 lambda,而是有效地重新创建大部分std::bind
.
要扩展 @T.C. 的注释,您可以通过更改创建的emplacer
的类型来使代码工作。
auto boundFunction = std::bind(emplacer<T, Args&...>,
std::forward<Args>(args)...);
请注意 Args
之后的&
。原因是您将右值传递给 emplace
函数,该函数反过来创建emplacer(int&&)
。 然而,std::bind
总是传递一个左值(因为它来自其内部(。更改到位后,签名将更改为 emplacer(int&)
(引用折叠后(,这可以绑定到左值。
- FLTK 2.0构建和演示,适用于VS2019的2011年左右的代码库
- "string.h"在构建适用于iOS的qt应用程序中找不到消息
- 为什么 std::erase(std::erase_if) 不是适用于<algorithm>任何容器的模板?
- 为什么这适用于 G++ 而不是 CLANG?
- 为什么不区分大小写适用于 std::unordered_set的 std::hash 函数?
- 声明适用于 auto,但不能显式声明类型?
- 什么是通用运行时组件 #ifdef 适用于Windows(UWP)而不是iOS
- Windows 上的 Cmake 不添加共享库路径(适用于 linux)
- Typedef适用于结构,但不适用于枚举,仅适用于C++
- 为什么函数模板不理解 NULL,但适用于 nullptr
- 链接器读取库,但在其中找不到符号?未解析的外部符号,但仅适用于 Win32 而不是 x64
- Netbeans 8.1(适用于 C/C++)找不到我的编译器(gcc-6.0.0 开发版本)
- random_shuffle() 适用于 Windows,但不能在 Linux 上编译
- C++指针到方法模板推导在面向 x86 时不编译,但适用于 x64
- 不在异常中嵌入std::字符串的规则是否仍然适用于move构造函数
- 移动文件夹Windows c++:适用于Vista及以上版本,不包括XP
- 适用于Android的Qt找不到任何兼容的设备答案找到但不知道如何
- 在Windows 7上不起作用,但它适用于Windows 8 dll。
- 可变参数模板转换为 std::function<R(ARGS...)>适用于 GCC 而不是 MSVC2013,为什么?
- C++11 cmath 函数不在 std 命名空间中,适用于 android NDK w/gcc-4.8 或 clang