提问者:小点点

简化使用CRTP模式的类声明


我使用了“奇怪地重复出现的模板模式”来继承一个静态成员变量和一个实例getter。 听起来有些做作,但是这个项目将会有很多很多的子类,它们需要尽可能轻松地声明,而不需要重复代码。

我想出了以下方法,效果很好:

#include <iostream>
#include <memory>
#include <string>
#include <vector>

struct Base {
  virtual void printStrings() = 0;
};

template <typename T>
struct static_strings: Base {
  static std::vector<std::string> strings;

  static void addString(const std::string& s) {
    strings.push_back(s);
  }

  virtual void printStrings() {
    for (auto s: T::strings) {
      std::cout << s << std::endl;
    }
  }
};

template <typename T> std::vector<std::string> static_strings<T>::strings;

struct Sub1: static_strings<Sub1> {};
struct Sub2: static_strings<Sub2> {};

int main() {
  Sub1::addString("test 1");
  std::shared_ptr<Base> s1 = std::make_shared<Sub1>();
  Sub2::addString("test 2");
  std::shared_ptr<Base> s2 = std::make_shared<Sub2>();
  std::cout << "s1: ";
  s1->printStrings();
  std::cout << "s2: ";
  s2->printStrings();
  return 0;
}

但是,我想进一步简化新子类的声明,因为现在我必须复制声明并在粘贴行中更改类名两次(structsub3:static_strings{};)。 我可以使用宏,但我想知道是否有一个非宏(模板?) 怎么做?


共2个答案

匿名用户

您可以轻松地更改basetp,使用一组要派生的模板参数:

template <typename T, template<typename> typename... OtherBases>
struct Base : OtherBases<T>... {
  [...]
};

struct Sub1: Base<Sub1, static_strings> {};
struct Sub2: Base<Sub2, static_strings> {};

这不是一个很大的胜利,但如果你有更多的crtp基础类,这可能会有所帮助。 但是,我无法想像没有宏就能保存剩余重复的方法。

这里是实时代码。

对于shared_pointer,您需要从一个额外的非模板基类派生。

struct AbstractBase {
    virtual ~AbstractBase() = default;
    virtual void printStrings() = 0;
};

template <typename T, template<typename> typename... OtherBases>
struct Base : AbstractBase, OtherBases<T>... {... };

然后从该指针创建共享指针:

std::shared_ptr<AbstractBase> s1 = std::make_shared<Sub1>();
std::shared_ptr<AbstractBase> s2 = std::make_shared<Sub2>();

请参阅此处获得更新示例。

匿名用户

您可以使用帮助程序类型:

struct helper : foo<helper>, bar<helper> {};

仅仅这样做是不行的,因为从它继承的所有类型将共享相同的基类。 为了避免重复类型名,可以引入一个虚拟模板参数:

template <typename T>
struct foo {};

template <typename T>
struct bar{};

template <int x>
struct helper : foo<helper<x>>, bar<helper<x>> {};

using Sub1 = helper<1>;
using Sub2 = helper<2>;
int main () {

}

这就是不需要宏就能做到的。 对于宏,您可以使用__counter__(gcc有它,不确定其他的)来获取不同的ints:

using SubA = helper<__COUNTER__>;
using SubB = helper<__COUNTER__>;