一些上下文: 我正在编写一个序列化库,并希望将要序列化的对象的所需更改降至最低。我找到了一些很棒的示例,例如MetaStuff,但想自己为模板练习和自定义实现它。
我的问题:
我想为派生类重载operator <<。 派生类继承自基类,而基类是它们自身的专用模板(通过CRTP)。 到目前为止,我要输出的成员变量是公共的,因此让我们无需声明操作员朋友。
template<typename Class>
Class Base {
protected:
Base(std::string name) : name(name){}
public:
const std::string name;
static int f(const Class& instance){
return instance.a;
}
};
(我简化了很多步骤,只保留必要的元素)
struct Derived : public Base<Derived>{
Derived():Base<Derived>("Derived"){}
int a;
static bool registerClass(){ return true; //called by Base<Derived> }
};
我遇到了3个冲突的问题(取决于我尝试的解决方案):
问题1:模版解析不明确
template <typename Class>
std::ostream& operator<<(std::ostream& os, const Class& obj)
{
os << obj.name << "[ ";
os << Base<Class>::f(obj);
os << "]";
return os;
};
在这里,我遇到了模棱两可的过载问题。模板函数具有与通用函数完全相同的原型。 Code & Compile error
问题2:类型切片
template <typename Class>
std::ostream& operator<<(std::ostream& os, const Base<Class>& obj)
{
os << obj.name << "[ ";
os << Base<Class>::f(obj);
//will not compile, since obj is not a Class object anymore.
os << "]";
return os;
};
如注释中所述,对象在传递给函数时被切片,然后我无法将其作为静态Base函数f的参数传递。 Code & Compile error
问题3:功能模板专业化不正确
不可能对模板函数进行部分专业化,并且要避免整体对函数模板进行专业化:编译器将始终倾向于使用Base模板。 (请参阅此不错的article)
还剩下什么?
我正在考虑将解决方案2与static_cast一起使用,但会觉得很丑吗?我尝试了其他一些解决方案,这些都不值得一提。
还有其他线索吗?
(由于我希望对派生类的修改尽量少,所以我不想添加虚函数等)
答案 0 :(得分:2)
正确的解决方案是尝试1和2中混合使用代码。
template <
typename Class,
typename = typename std::enable_if< std::is_base_of<Base<Class>, Class>::value >::type
>
std::ostream& operator<<(std::ostream& os, const Class& obj)
{
os << obj.name << "[ ";
os << Base<Class>::f(obj);
os << " ]";
return os;
}
Class&
而不是Base<Class>&
,因为您(可能)想对Class
的成员进行操作。enable_if
是为了使运算符仅适用于从Base
派生的类型;这样可以防止它蔓延到其他重载(通常,重载和模板混合不良),因为这样的模板重载可能会匹配任何内容。Class
传递给Base
,以获得正确的f
。还有一个小字条;使用现代的编译器,您可以将其拼写得更好:
typename = std::enable_if_t<std::is_base_of_v<Base<Class>, Class>>
答案 1 :(得分:1)
CRTP通常使用static_cast
:
template <typename T>
class Base
{
protected:
explicit Base(std::string name) : name(std::move(name)){}
const T& asDerived() const { return static_cast<const T&>(*this); }
T& asDerived() { return static_cast<T&>(*this); }
public:
const std::string name;
static int f(const Base<T>& instance){
return instance.asDerived().a;
}
};
等等
template <typename T>
std::ostream& operator<<(std::ostream& os, const Base<T>& obj)
{
os << obj.name << "[ ";
os << Base<T>::f(obj);
os << "]";
return os;
}
请注意,Base::f
将不再是static
:
template <typename T>
class Base
{
// ...
int f() const { return asDerived().a; }
// ...
};
template <typename T>
std::ostream& operator<<(std::ostream& os, const Base<T>& obj)
{
return os << obj.name << "[ " << obj.f() << "]";
}