简化使用 CRTP 模式的类的声明

Simplify declarations of classes that use the CRTP pattern

本文关键字:声明 模式 CRTP      更新时间:2023-10-16

我使用"奇怪的重复模板模式"来继承静态成员变量和实例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;
}

但是,我想进一步简化新子类的声明,因为现在我必须复制声明并在粘贴的行中两次更改类名 (struct Sub3: static_strings<Sub3> {};(。我可以使用宏,但我想知道是否有非宏(模板?(方法来做到这一点?

您可以轻松更改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 有它,不确定其他宏(来获得不同的int

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