我有一个模板类,我想专门针对许多不同类型。比如:
#include <type_traits>
#include <string>
#include <string_view>
// To allow static assert for invalid branches
template <typename T>
inline constexpr bool invalid = false;
template <typename T>
struct TypeMeta
{
using type = T;
using intermediate_type = T;
static consteval auto marshal_string() {static_assert(invalid<T>, "Not implemented for type.");}
static constexpr auto convert(intermediate_type &&value) {return type{value};}
static constexpr bool needs_conversion = !std::is_same_v<type, intermediate_type>;
};
我希望能够像这样覆盖此类的部分内容:
struct TypeMeta<std::string>
{
using intermediate_type = const char *;
static consteval std::string_view marshal_string() {return "s";}
};
然后进一步将其用于以下用途:
template <typename T>
T extract_from(void *data)
{
using meta_t = TypeMeta<T>;
if constexpr (meta_t::needs_conversion) {
typename meta_t::intermediate_type tmp;
tmp = *(static_cast<meta_t::intermediate_type *>(data));
return meta_t::convert(tmp);
} else {
return *(static_cast<meta_t::type *>(data));
}
}
显然,这对
std::string
不起作用,因为我还没有在我的专业领域中重新定义 type
、needs_conversion
和 convert
。
现在,我可以通过使用 CRTP 继承一个基类来轻松解决这个问题。比如:
template <typename T>
struct TypeMeta;
template <typename T>
struct TypeMetaBase
{
using type = T;
using intermediate_type = T;
static consteval auto marshal_string() {static_assert(invalid<T>, "Not implemented for type.");}
static constexpr type convert(intermediate_type &&value) {return type{value};}
static constexpr bool needs_conversion = !std::is_same_v<typename TypeMeta<T>::type, typename TypeMeta<T>::intermediate_type>;
};
template <typename T>
struct TypeMeta : public TypeMetaBase<T>{};
template <>
struct TypeMeta<std::string> : public TypeMetaBase<std::string>
{
using intermediate_type = const char *;
static consteval std::string_view marshal_string() {return "s";}
};
效果很好。我认为你必须记住从每种类型的基类继承,这有点不好,但这可能是不可避免的。 然而,当您只想专门化父级实际需要的一两个函数时,就会出现“真正的”问题。我想做这个:
template <>
consteval std::string_view TypeMeta<int>::marshal_string() {return "i"};
但是这是不允许的:
<source>:xx:yy: error: no member function 'marshal_string' declared in 'TypeMeta<int>'
xx | consteval auto TypeMeta<int>::marshal_string() {return "i";}
我可以通过在模板中复制该方法来解决这个问题:
template <typename T>
struct TypeMeta : public TypeMetaBase<T>
{
static consteval auto marshal_string() {static_assert(invalid<T>, "Not implemented for type.");}
};
这意味着像
std::string
这样的专门结构体如果没有定义,将会回退到使用
TypeMetaBase
的方法,而只有一些专门方法的结构体,比如
int
结构体将使用 TypeMeta
的方法对于那些不专业的人。但这意味着我必须不必要地重复代码。我不喜欢那样。它很容易失去同步,并且启动时会出现不必要的膨胀。有什么方法可以避免重复方法,同时允许最少的额外输入?我想我可以要求任何专业化声明自己的结构,但有许多不同的类型,其中大多数只需要一个重写的方法,我宁愿保持它尽可能简洁。
一个额外的想法可能是使用宏进行额外的结构声明以避免样板,但如果可能的话,我宁愿避免使用宏。
我基本上只是想获得一个不错的 C++ 解决方案,它不涉及滥用预处理器或复制粘贴代码。
您可以将每个组件拆分为自己的 type_traits: