首页 > 解决方案 > 在运行时询问变体中的替代方案是否满足某个概念

问题描述

我有以下代码,我尝试检测变体中的替代概念(在这种情况下为成员函数)。它冗长且相对丑陋。有没有更好的方法来做到这一点?请注意,我不想使用继承,也不想使用静态多态(假设在编译时不知道使用的引擎)。

简单来说,我正在寻找if constexpr(仅在编译时检查概念)和std::holds_alternative(只能检查特定类型是否为 in variant,而不是满足概念的任何类型是否为 in variant)的运行时组合。

#include <iostream>
#include <variant>


struct simple_engine1{

};
struct simple_engine2{

};
struct complex_engine1{
    void reduce_thrust(int perc){
        std::cout<<"reducing thrust " << perc << "% " << std::endl;
    }
};
struct complex_engine2{
    void reduce_thrust(int perc){
        std::cout<<"reducing thrust " << perc << "% " << std::endl;
    }
};
template< class, class = std::void_t<> >
struct has_reduce_thrust : std::false_type { };
template< class T >
struct has_reduce_thrust<T,
           std::void_t<decltype( std::declval<T>().reduce_thrust(42) )>
       > : std::true_type { };
static_assert(!has_reduce_thrust<simple_engine1>::value);
static_assert(!has_reduce_thrust<simple_engine1>::value);
static_assert(has_reduce_thrust<complex_engine1>::value);

struct visitor{
  template<typename T>
  void operator()(T& t){
      dispatch(t, has_reduce_thrust<T>{});

  }
  template<typename T>
  void dispatch(T& t, std::true_type /*has_reduce_thrust*/){
      t.reduce_thrust(perc);
      reduced_thrust=true;
  }
  template<typename T>
  void dispatch(T& , std::false_type){
      reduced_thrust=false;
  }
  int perc = 0;
  bool reduced_thrust = false;  
};
// tries to reduce speed by reducing thrust if engine supports it, if not
// it fires reverse engines(more expensive wrt fuel usage)
void reduce_speed(std::variant<simple_engine1, simple_engine2, complex_engine1, complex_engine2>* var_engine){
    visitor v;
    v.perc = 47;
    std::visit(v, *var_engine);
    if (v.reduced_thrust) {
        std::cout << "reduced thrust\n"; 
    } else {
        std::cout << "activating reverse engines\n";
    }
}
int main() {
    std::variant<simple_engine1, simple_engine2, complex_engine1, complex_engine2> var_engine{simple_engine1{}};
    reduce_speed(&var_engine);
    var_engine = complex_engine2{};
    reduce_speed(&var_engine);
    var_engine = simple_engine2{};
    reduce_speed(&var_engine);
    var_engine = complex_engine2{};
    reduce_speed(&var_engine);
} 

标签: c++variantc++-concepts

解决方案


您可以使用以下方法大大简化访问者if constexpr

struct visitor{
  template<typename T>
  void operator()(T& t) {
    if constexpr (has_reduce_thrust<T>::value) {
      t.reduce_thrust(perc);
      reduced_thrust = true;
    }
    else {
      reduced_thrust = false;
    }
  }

  int perc = 0;
  bool reduced_thrust = false;  
};

然后,您可以通过为 的任一分支接受任何谓词和两个函数来进一步抽象if constexpr

template <template <class, class... /*SFINAE friendly*/> class TypePred,
          class MatchedFunc, class UnmatchedFunc>
class predicated_visitor {
 public:
  predicated_visitor(MatchedFunc matchedFunc, UnmatchedFunc unmatchedFunc)
      : _matchedFunc(matchedFunc), _unmatchedFunc(unmatchedFunc) {}

  template <typename T>
  void operator()(T& t) {
    if constexpr (TypePred<T>::value)
      _matchedFunc(t);
    else
      _unmatchedFunc(t);
  }

 private:
  MatchedFunc _matchedFunc;
  UnmatchedFunc _unmatchedFunc;
};

template <template <class, class... /*SFINAE friendly*/> class TypePred,
          class F1, class F2>
auto makePredicatedVisitor(F1 f1, F2 f2) {
  return predicated_visitor<TypePred, F1, F2>(f1, f2);
}

我觉得生成的代码非常好:

void reduce_speed(std::variant<simple_engine1, simple_engine2, complex_engine1,
                               complex_engine2>* var_engine) {
  int perc = 47;
  bool reducedThrust = false;

  auto reduceableThrustAction = [perc, &reducedThrust](auto& t) {
    t.reduce_thrust(perc);
    reducedThrust = true;
  };
  auto alternativeAction = [](auto& t) {
  };  // Could explicitly set reduceThrust to false for clarity.

  auto thrust_visitor = makePredicatedVisitor<has_reduce_thrust>(
      reduceableThrustAction, alternativeAction);

  std::visit(thrust_visitor, *var_engine);

  if (reducedThrust) {
    std::cout << "reduced thrust\n";
  } else {
    std::cout << "activating reverse engines\n";
  }
}

演示

此示例编译为与您的基本相同的汇编代码,但可以以您喜欢的任何方式重用。

我为不一致的大小写道歉...


推荐阅读