C++模板化类上继承的运算符'<<'
C++ inherited operator '<<' on templated class
我有以下C++代码,它们说明了我的问题。 我的目标是覆盖继承类中的流运算符,以便允许我根据对象类型打印特定的流:
#include <iostream>
#include <unordered_set>
using namespace std;
template <typename T>
class Base {
public:
Base(){}
Base(T n): value_(n){}
friend inline ostream &operator<<(ostream &os, const Base &b) {
b.to_str(os);
return os;
}
protected:
T value_;
// All object should implement this function
virtual void to_str(ostream& os) const {
os << value_;
}
};
template <typename T>
class Child: public Base<T> {
public:
Child(T n): Base<T>(n){}
protected:
void to_str(ostream& os) const override {
os << "{";
for (auto v = this->value_.begin(); v != this->value_.end(); v++) {
if(v != this->value_.begin())
os << ",";
os << (*v);
}
os << "}";
}
};
int main()
{
Base<string> b("base");
Child<unordered_set<string>> c({"child"});
cout << "b: " << b << endl;
cout << "c: " << c << endl;
return 0;
}
目前代码尚未编译:
main.cpp:31:16: error: no match for ‘operator<<’ (operand types are ‘std::ostream {aka std::basic_ostream}’ and ‘const std::unordered_set >’)
编译器似乎使用了 base 的虚拟方法to_str((,而不是 Child 类的重写方法。 如果我注释基本to_str((函数的主体,那么它会编译并打印 Child 类的正确结果unordered_set但随后它不会使用基本实现打印任何内容。 所以覆盖是有效的,但是为什么当base to_str((有一个主体时它不编译?
如何强制编译器使用派生的编译器(Child(中的编译器?
问候
即使从未运行过基to_str
也会被编译。
所以
virtual void to_str(ostream& os) const {
os << value_;
}
编译失败。
当您使用 vtable 创建类型时,即使没有调用它们,也会填充其条目(好吧,我相信标准说"可以"(。 这与"普通"模板类不同,在"普通"模板类中,未使用的方法的主体被"跳过"。
您可能希望 CRTP 用于编译时多态性。
template <class T, class D_in=void>
class Base {
using D=std::conditional_t< std::is_same<D_in,void>{}, Base, D_in >;
public:
Base(){}
Base(T n): value_(n){}
friend inline ostream &operator<<(ostream &os, const Base &b) {
static_cast<D const&>(b).to_str(os);
return os;
}
protected:
T value_;
// All object should implement this function
void to_str(ostream& os) const {
os << value_;
}
};
template <typename T>
class Child: public Base<T, Child<T>> {
friend class Base<T, Child<T>>;
public:
Child(T n): Base<T>(n){}
protected:
void to_str(ostream& os) const {
os << "{";
for (auto v = this->value_.begin(); v != this->value_.end(); v++) {
if(v != this->value_.begin())
os << ",";
os << (*v);
}
os << "}";
}
};
或诸如此类。
虚拟调用确实发生了。 您可以通过将代码更改为
template <typename T>
class Base {
public:
Base(){}
Base(T n): value_(n){}
friend inline ostream &operator<<(ostream &os, const Base &b) {
b.to_str(os);
return os;
}
protected:
T value_;
virtual void to_str(ostream& os) const = 0;
};
template <typename T>
class Child: public Base<T> {
public:
Child(T n): Base<T>(n){}
protected:
void to_str(ostream& os) const override {
os << "{";
for (auto v = this->value_.begin(); v != this->value_.end(); v++) {
if(v != this->value_.begin())
os << ",";
os << (*v);
}
os << "}";
}
};
int main()
{
Child<unordered_set<string>> c({"child"});
cout << "c: " << c << endl;
return 0;
}
实际发生的是,在Base
编译器将消除
Base<unordered_set<string>>::to_str
并且该函数无效,因为未定义os << value_
。 如您所见,您需要将其设置为纯虚拟,或者放入一个无论value_
是什么都可以编译的存根。
这是我最终通过使用"纯虚拟解决方案"实现的, 用一些更基本的类型进行了测试:
#include <iostream>
#include <unordered_set>
using namespace std;
template <typename T>
class Base_ {
public:
Base_(){}
Base_(T n): value_(n){}
friend inline ostream &operator<<(ostream &os, const Base_ &b) {
b.to_str(os);
return os;
}
protected:
T value_;
// All object should implement this function
virtual void to_str(ostream& os) const = 0;
};
template <typename T>
class Base: public Base_<T> {
public:
Base(){}
Base(T n): Base_<T>(n){}
protected:
// All object should implement this function
void to_str(ostream& os) const override {
os << this->value_;
}
};
template <typename T>
class Child: public Base_<T> {
public:
Child(T n): Base_<T>(n){}
protected:
void to_str(ostream& os) const override {
os << "{";
for (auto v = this->value_.begin(); v != this->value_.end(); v++) {
if(v != this->value_.begin())
os << ",";
os << (*v);
}
os << "}";
}
};
template <typename T>
class Boolean: public Base_<T> {
public:
Boolean(T n): Base_<T>(n){}
protected:
void to_str(ostream& os) const override {
os << (this->value_ ? "true" : "false");
}
};
int main()
{
Base<string> s("string");
Base<int> i(42);
Boolean<bool> b(true);
Child<unordered_set<string>> u({"child1", "child2"});
cout << "s: " << s << endl;
cout << "i: " << i << endl;
cout << "b: " << b << endl;
cout << "u: " << u << endl;
return 0;
}
结果:
S:字符串
I:42
B:真
U:{子项 2,子项 1}
相关文章:
- 如何防止clang格式在流运算符调用之间添加换行符<<
- 如何显式调用运算符<<
- 为什么COUT在朋友函数中不起作用,该功能超载了操作员&lt;&lt;这是一个iStream运算符
- C++运算符<<调用::ostream而不是std::osttream
- BOOST ::变体无法解决运算符&lt;&lt;对于STD :: Ostream
- 过载输出<<用于类的运算符,以打印其中的元组
- C++ostream:没有运算符匹配<<&应在'&'代币
- 重载运算符<<:此运算符函数的参数太多
- C++继承运算符<<
- 重载运算符<<用于模板类.即使使用好友关键字也无法获得私人会员
- 如何过载<<用于YAML::Emitter的运算符,以序列化包含另一个自定义类的向量的自定义类
- 为什么字符串流运算符<<擦除原始值
- 关于使用运算符<<为新手提供C++中的模板
- 我已经完成了<<运算符重载,但它'It’不起作用
- 重载运算符<<输出地址而不是数据成员
- 错误:没有匹配'运算符<<"在'std::cout
- 重载运算符<<用于ostream语法
- 当运算符<存在时,为什么要定义 LT?
- log4cxx访问异常,使用<<运算符和宏
- 重载<<运算符错误C2804:二进制'运算符<<'参数太多