【问题标题】:How to implement an easy_bind() that automagically inserts implied placeholders?如何实现自动插入隐含占位符的easy_bind()?
【发布时间】:2013-02-08 01:45:45
【问题描述】:

我最近在网上发现了这个漂亮的 sn-p - 它允许您绑定而无需传递显式占位符:

template <typename ReturnType, typename... Args>
std::function<ReturnType(Args...)> 
easy_bind(ReturnType(*MemPtr)(Args...))
{
  return [=]( Args... args ) -> ReturnType { return (*MemPtr)( args... ); };
}

这个版本很好用,没有参数:

auto f1 = easy_bind( (std::string(*)(A&,A&))&Worker::MyFn );

稍后调用:

std::string s = f1( *p_a1, *p_a2 );

问题

是否可以修改代码以使用最多 n 个参数,用占位符填充 2-n(在这种情况下)?例如,这个应该有一个占位符:

auto f2 = easy_bind( (std::string(*)(A&,A&))&Worker::MyFn, *p_a1 );     

稍后调用:

std::string s = f2( *p_a2 );

奖金

最终,最好有这样的东西(它不插入占位符,因为它会用完最后一个),但我认为它不适用于此实现(无法与签名进行模式匹配,我想想):

auto f3 = easy_bind( f2, *p_a2 );     

稍后调用:

std::string s = f3();

底线是,如果有一个不需要放置占位符的 bind 版本会很好 - 它在通用 TMP 代码中非常有用。

【问题讨论】:

  • 为什么不只是return { MemPtr };
  • @KerrekSB 在没有传入任何内容的情况下工作 - 如果我开始传入 partial 参数列表怎么办?
  • 虽然没有 显式 占位符,但它们在演员表中存在(我个人觉得阅读起来不如占位符列表好)。
  • 我不知道如何看待(std::string(*)(A&amp;,A&amp;))&amp;Worker::MyFn。这合法吗?
  • @KerrekSB 在我的测试用例中需要它(解决重载问题 - 我知道,它看起来像一团糟!) - 这部分测试结果很好。

标签: c++ templates c++11 variadic-templates


【解决方案1】:

使用indices trickthe ability to tell std::bind about your own placeholder types,这是我想出的:

#include <functional>
#include <type_traits>
#include <utility>

template<int I> struct placeholder{};

namespace std{
template<int I>
struct is_placeholder< ::placeholder<I>> : std::integral_constant<int, I>{};
} // std::

namespace detail{
template<std::size_t... Is, class F, class... Args>
auto easy_bind(indices<Is...>, F const& f, Args&&... args)
  -> decltype(std::bind(f, std::forward<Args>(args)..., placeholder<Is + 1>{}...))
{
    return std::bind(f, std::forward<Args>(args)..., placeholder<Is + 1>{}...);
}
} // detail::

template<class R, class... FArgs, class... Args>
auto easy_bind(std::function<R(FArgs...)> const& f, Args&&... args)
    -> decltype(detail::easy_bind(build_indices<sizeof...(FArgs) - sizeof...(Args)>{}, f, std::forward<Args>(args)...))
{
    return detail::easy_bind(build_indices<sizeof...(FArgs) - sizeof...(Args)>{}, f, std::forward<Args>(args)...);
}

Live example.

请注意,我要求easy_bind 的函数参数为std::function 类型,或可转换为std::function,以便我有明确的可用签名。

【讨论】:

  • +1 woohoo - tyvm - 它比上一个版本还要短:v.nice
  • @kfmfe04:我没有在这个索引中使用索引,因为您可以轻松地自己编写它们(参见链接或搜索 SO)。
  • @kfmfe04:请注意,我刚刚修复了占位符生成方式的一个小问题。
  • @kfmfe04:是的,detail 只不过是实现细节的约定。 std 中的代码是为了让 std::bind 知道我们的占位符。
  • “实时示例”链接已失效。你能展示示例用法吗?
【解决方案2】:

这让我很困扰,因为我不得不在当时不知道参数的情况下绑定一个函数。 (如这里所示的工厂How to implement serialization in C++

例如(假设 TSubClass::create 是静态的)

template<typename TFactoryClass, typename TArgs...>
class Factory
{
public:
    template<typename TSubClass>
    void register(int id)
    {
         _map.insert(std::make_pair(id, std::bind(&TClass::create, /*how to give TArgs as placeholders??*/)));
    }
}

相反,我能够将 std::bind 替换为 lambda 表达式,而无需使用所有这些辅助类!

template<typename TFactoryClass, typename TArgs...>
class Factory
{
public:
    template<typename TSubClass>
    void register(int id)
    {
         _map.insert(std::make_pair(id, [](TArgs... args) { TSubClass::create(args...); }));
    }
}

作为奖励,您还可以使用这种机制“绑定”到构造函数

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 2021-11-18
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2015-04-18
    • 2017-11-20
    相关资源
    最近更新 更多