尝试将静态多态性与动态多态性混合?

问题描述 投票:0回答:1

背景

  • Comparator
    用于比较两个
    unsigned long long
    数字。
  • DefaultComparator
    支持默认的相等运算。
  • CoarseGrainedComparator
    支持 某种科斯粒度相等运算,例如,如果两个数字的绝对值在 1 之间,则它们被视为相等。
struct Comparator {
  virtual ~Comparator() = default;
  virtual bool operator()(unsigned long long lhs, unsigned long long rhs) const = 0;
};

struct DefaultComparator : public Comparator {
  bool operator()(unsigned long long lhs, unsigned long long rhs) const override {
    return lhs == rhs;
  }
};

struct CoarseGrainedComparator : public Comparator {
  bool operator()(unsigned long long lhs, unsigned long long rhs) const override {
    static constexpr T kThres = 1;
    if (lhs < rhs) {
      return (rhs - lhs) <= kThres;
    } else {
      return (lhs - rhs) <= kThres;
    }
  }
};

我之所以采用OO编程方式,是因为我想将它们保存在一个哈希图中。所以,我需要一个通用类型,即

Comparator

using ComparatorPtr = std::shared_ptr<Comparator>;
template <typename ComparatorT>
ComparatorPtr ComparatorBuilder() {
  return std::make_shared<ComparatorT>();
}

class CompatorFactory : public Singleton<CompatorFactory> {
 public:
  void Register(unsigned int slot, ComparatorPtr comparator) {
    compators_.emplace(slot, comparator);
  }
  bool Get(unsigned int slot, ComparatorPtr& campator) {
    auto it = compators_.find(slot);
    if (it == compators_.end()) {
      return false;
    }
    campator = it->second;
    return true;
  }
 private:
  std::unordered_map<unsigned int, ComparatorPtr> compators_;
};

客户端代码如下:

// Register the comparator for a specific slot id(301)
CompatorFactory::GetInstance()->Register(301, ComparatorBuilder<CoarseGrainedComparator>());

// Prepare old value and new value
// ......

// Compare old value and new value based on specific comparator. 
ComparatorPtr comparator;
if (!CompatorFactory::GetInstance()->Get(slot_id, comparator)) {
  comparator = ComparatorBuilder<DefaultComparator>();
}
auto equal = (*comparator)(old_value, new_value);

// Post operation based on equal
// ......

到目前为止,代码运行良好。

问题

除了

unsigned long long
,还应该支持
double

  • 我可以再写一个Comparator来支持
    double
    ,但是会引入很多重复的代码。
  • 所以采用了泛型编程。
struct Comparator {
  virtual ~Comparator() = default;

  template <typename T>
  virtual bool operator()(const T& lhs, const T& rhs) const = 0;
};

struct DefaultComparator : public Comparator {
  template <typename T>
  bool operator()(const T& lhs, const T& rhs) const override {
    return lhs == rhs;
  }
};

struct CoarseGrainedComparator : public Comparator {
  template <typename T>
  bool operator()(const T& lhs, const T& rhs) const override {
    static constexpr T kThres = 1;
    if (lhs < rhs) {
      return (rhs - lhs) <= kThres;
    } else {
      return (lhs - rhs) <= kThres;
    }
  }
};

这导致了另一个问题:尝试将静态多态性与动态多态性混合是不可行的。

那么,我们如何解决这样的问题呢?

更好的解决方案可以混合静态电位和动态电位。

c++ oop generics polymorphism
1个回答
-1
投票

这个答案说明了如何使用类型擦除来模拟 C++ 中的“模板化虚拟”。它假设如下,

  1. 可以合理分离依赖动态多态和静态多态的行为。
  2. 有一小部分可预定义的操作仅依赖于静态多态性,即使在类型被擦除后也可以用作更复杂算法的构建块。
  3. 由于类型擦除而导致虚拟调度产生的额外成本不是问题。

代码使用

std::any
进行类型擦除,但也可以使用其他形式的类型擦除。

#include <cassert>
#include <any>

//Table for all the fundamental operations that solely depend on static polymorphism and can be used as building blocks after type erasure
struct ComparatorFuncTable{
    std::any (*from_int)(int);
    bool (*eq)(const std::any&, const std::any&);
    bool (*lt)(const std::any&, const std::any&);
    bool (*le)(const std::any&, const std::any&);
    std::any (*minus)(const std::any&, const std::any&);};

//C++14 variable template for the actual table
template <typename T>
ComparatorFuncTable comparatorFuncTable = {
    .from_int = +[](int v){return std::any{static_cast<T>(v)};},
    .eq = +[](const std::any& lhs, const std::any& rhs){return std::any_cast<T>(lhs) == std::any_cast<T>(rhs);},
    .lt = +[](const std::any& lhs, const std::any& rhs){return std::any_cast<T>(lhs) < std::any_cast<T>(rhs);},
    .le = +[](const std::any& lhs, const std::any& rhs){return std::any_cast<T>(lhs) <= std::any_cast<T>(rhs);},
    .minus = +[](const std::any& lhs, const std::any& rhs){return std::any{T{std::any_cast<T>(lhs) - std::any_cast<T>(rhs)}};},
};

struct Comparator {
  virtual ~Comparator() = default;

  //While non-virtual by itself, call to run() performs the dynamic dispatch
  template <typename T>
  bool operator()(const T& lhs, const T& rhs) const {
    return run(lhs, rhs, comparatorFuncTable<T>);
  }
protected:
  virtual bool run(const std::any& lhs, const std::any& rhs, const struct ComparatorFuncTable& table) const = 0;

};

struct DefaultComparator : public Comparator {
protected:
  virtual bool run(const std::any& lhs, const std::any& rhs, const ComparatorFuncTable& table) const override {
    return table.eq(lhs,rhs);
  }
};

struct CoarseGrainedComparator : public Comparator {
protected:
  virtual bool run(const std::any& lhs, const std::any& rhs, const ComparatorFuncTable& table) const override {
    //Use the type erased operations to build the complex operation
    std::any kThres = table.from_int(1);
    if (table.lt(lhs, rhs)) {
      return table.le(table.minus(rhs, lhs), kThres);
    } else {
      return table.le(table.minus(lhs, rhs), kThres);
    }
  }
};

int main(){
  const Comparator& cmp1=DefaultComparator{};
  const Comparator& cmp2=CoarseGrainedComparator{};
  assert(cmp1(2.0, 2.0) && cmp2(2.0, 2.0));
  assert(cmp1(2.0f, 2.0f) && cmp2(2.0f, 2.0f));
  assert(!cmp1(2.0f, 2.5f) && cmp2(2.0f, 2.5f));
}
© www.soinside.com 2019 - 2024. All rights reserved.