选择第一个有效表达式

时间:2017-08-15 22:34:47

标签: c++ c++14 sfinae

我正在尝试编写一个允许我从表达式列表中运行第一个有效表达式的函数,其中一些表达式可能无效。这样,,例如,允许对容器进行统一处理,其中一些可能有emplace_back,但有些只有emplace

这是我当前的实现,使用is_detected

template <typename F1, typename... Fs>
class select {
  template <typename... Args>
  using expr_t = decltype(F1{}(std::declval<Args>()...));
  template <typename... Args>
  constexpr static bool ok1 = is_detected<expr_t,Args...>::value;
public:
  template <typename... Args, std::enable_if_t<ok1<Args...>>* = nullptr>
  inline decltype(auto) operator()(Args&&... args) {
    return F1{}(std::forward<Args>(args)...);
  }
  template <typename... Args, std::enable_if_t<!ok1<Args...>>* = nullptr>
  inline decltype(auto) operator()(Args&&... args) {
    static_assert(sizeof...(Fs),"cannot select valid implementation");
    return select<Fs...>{}(std::forward<Args>(args)...);
  }
};

#define MEMFCN_SFINAE_WRAP(NAME,EXPR) \
  struct NAME { \
    template <typename X, typename... Args> \
    inline auto operator()(X&& x, Args&&... args) \
    -> decltype(EXPR) { return EXPR; } \
  };

这是一个用法示例:

#include <iostream>
#include <string>
#include <vector>
#include <map>

MEMFCN_SFINAE_WRAP(emplace_back, x.emplace_back(std::forward<Args>(args)...));
MEMFCN_SFINAE_WRAP(emplace, x.emplace(std::forward<Args>(args)...));

int main(int argc, char* argv[]) {
  std::vector<std::pair<std::string,std::string>> v;
  std::map<std::string,std::string> m;

  select<emplace_back,emplace>{}(v,std::make_pair("hello","world"));
  select<emplace_back,emplace>{}(m,std::make_pair("hello","world"));
}

我的问题是,是否可以实现更优雅的实现或抽象。我特别不喜欢我重复EXPR两次的事实,一次是decltype的返回类型演绎,另一次是函数体的重复演绎。

将此功能与容器上的成员函数一起使用只是一个示例。 一般的想法是使用此构造指定一个默认代码片段列表,以尝试先验未知参数。 另一个例子是有条件地使用std::cout << x;打印,或者打印x的类型,或者某些默认消息。

2 个答案:

答案 0 :(得分:0)

为什么不重用宏的第一个参数??

如:

#define MEMFCN_SFINAE_WRAP(NAME)                         \
  struct NAME {                                          \
    template <typename X, typename... Args>              \
    inline auto operator()(X&& x, Args&&... args)        \
    -> decltype(x. NAME (std::forward<Args>(args)...))   \
       { return x. NAME (std::forward<Args>(args)...); } \
  };

你可以使用宏编程技巧来获得一个带有可变数量参数的宏,这适用于gcc和ms编译器。

#define MEMFCN_SFINAE_WRAP_IMPL(NAME, EXPR, ...)         \
    struct NAME {                                        \
      template <typename X, typename... Args>            \
      inline auto operator()(X&& x, Args&&... args)      \
      -> decltype(EXPR) { return EXPR; }                 \
    };

#define MEMFCN_SFINAE_WRAP(NAME, ...)                    \
    MEMFCN_SFINAE_WRAP_IMPL(NAME, ## __VA_ARGS__, x.NAME(std::forward<Args>(args)...)) 

// call as    

MEMFCN_SFINAE_WRAP(emplace_back);

// or
MEMFCN_SFINAE_WRAP(emplace, x.emplace(std::forward<Args>(args)...));

答案 1 :(得分:0)

最重要的评论由Justin提供给

  

查看Boost Hana

最具表现力地建模这种情况的抽象是在maybe monad的上下文中放置一个可能有效的表达式,然后组合生成的对象(即定义绑定运算符,因为它是&#39; s在Haskell中调用)运行第一个maybe<function>,而不是nothing(即有效)。那是hana::sfinae所做的,至少是第一部分,放在maybe背景中。

在我看来,这是一个更好的抽象,因为它建立在较小的想法上,可以用更多的方式组成,允许解决更大类的问题