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

标签 c++ templates c++11

让我先解释一下我试图完成的事情。我需要创建一个类型删除仿函数(使用模板和虚函数),它能够在我正在开发的 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/

相关文章:

c++ - 模板类中方法的部分特化

C++11 move 构造函数有副作用

c++ - operator<< 智能指针重载

c++ - 如何在构造函数中初始化位域

c++ - Qt - 如何在行编辑中捕捉键盘事件?

c++ - 您应该将项目迁移到 C++11 吗?

c++ - 概念与复制构造函数冲突的泛型编程

c++ - 从 matlab 中的二进制文件读取 float 不正确

c++ - 如何创建仅包含主文件和模板 header 的 makefile (C++)

c++ - vector 保存类对象,类对象每个对象包含 3 个字符串。如何找到特定字符串,然后删除整个元素?