在阅读了返回类型的C++自动推导和C++:模板类的向量之后,我仍然想知道如何对对象执行泛型操作(例如操作符<<重载)。我的代码看起来像
#include <map>
#include <memory>
#include <string>
#include <iostream>
/**
* Abstract placeholder for Cache polymorphism
*/
class ICache
{
public:
virtual void update() {};
friend std::ostream & operator << (std::ostream & out, const ICache & IC)
{
out << "you should never print this";
}
};
/**
* Concrete. Coupling a name with some cached value
*/
template<typename T>
class Cache :
public ICache
{
const std::string m_name;
T m_cached;
public:
Cache(const std::string & name) :
m_name(name),
m_cached(0)
{}
void update() override
{
// m_cached is synced with remote resource; action depends both from T and m_name
}
void set(const T t)
{
std::cout << m_name << " setting " << +m_cached << " -> " << +t << std::endl;
m_cached = t;
}
inline T get() const noexcept { return m_cached; }
friend std::ostream & operator << (std::ostream & out, const Cache & O)
{
out << "Cache<" << O.m_name << ", " << O.m_cached << ">";
}
};
class CacheMap
{
std::map<std::string, std::unique_ptr<ICache>> m_map;
template<typename T>
Cache<T>* _get_ptr(const std::string & name) const
{
return reinterpret_cast<Cache<T>*>(m_map.at(name).get());
}
public:
template<typename T>
T get(const std::string & name) const
{
return _get_ptr<T>(name)->get();
}
template <typename T>
void set(const std::string & name, T t)
{
_get_ptr<T>(name)->set(t);
}
template <typename T>
void insert(const std::string & name, T def = 0)
{
std::unique_ptr<ICache> up = std::make_unique<Cache<T>>(name);
m_map.insert({name, std::move(up)});
set<T>(name, def);
}
friend std::ostream & operator << (std::ostream & out, const CacheMap & OM)
{
out << "OM{";
for (const auto & IO : OM.m_map)
out << IO.first << ": " << *(IO.second.get()) << ", "; // ver1
// out << IO.first << ": " << (IO.second->get()) << ", "; // ver2
out << "}";
return out;
}
};
int main()
{
CacheMap m;
int i= 70000;
m.insert<int>("i", 69999);
m.insert<short>("s", 699);
m.insert<char>("c", 69);
m.set("i", i);
std::cout << m << std::endl;
}用尾随//ver1标记的行显示you should never print this,这是有意义的;我正在处理std::unique_ptr<ICache>对象。
用尾随//ver2标记的行根本不会编译,这也是有意义的。
我想要做的是在运行时(hmm听起来很糟糕)自动检测给定地图键的正确的T,以便触发正确的reinterpret_cast<>并检索m_cached值。
编辑1
如果使用g++ -O3编译,则ver1行将导致分段冲突。
发布于 2020-01-02 11:58:35
只需使用一个虚拟函数。将变量从Cache<int>指针类型转换为ICache指针时,会丢失有关它的编译时间信息。信息丢失了。您可以在您的dynamic_cast中使用friend ICache::operator<<处理所有不同的类型..。要正确解析类型信息,请使用virtual函数--即。与每个类绑定的唯一数据。
#include <map>
#include <memory>
#include <string>
#include <iostream>
class ICache
{
public:
virtual ~ICache() {};
virtual void update() {};
// -------- HERE --------------
virtual std::ostream& printme(std::ostream & out) const = 0;
friend std::ostream& operator << (std::ostream & out, const ICache & IC) {
return IC.printme(out);
}
};
template<typename T>
class Cache : public ICache {
const std::string m_name;
T m_cached;
public:
Cache(const std::string & name): m_name(name), m_cached(0) {}
void update() override {}
void set(const T t) {
std::cout << m_name << " setting " << +m_cached << " -> " << +t << std::endl;
m_cached = t;
}
inline T get() const noexcept { return m_cached; }
std::ostream& printme(std::ostream & out) const override {
out << "Cache<" << m_name << ", " << m_cached << ">";
return out;
}
};
class CacheMap {
std::map<std::string, std::unique_ptr<ICache>> m_map;
template<typename T>
Cache<T>* _get_ptr(const std::string & name) const {
return dynamic_cast<Cache<T>*>(m_map.at(name).get());
}
public:
template<typename T>
T get(const std::string & name) const {
return _get_ptr<T>(name)->get();
}
template <typename T>
void set(const std::string & name, T t) {
_get_ptr<T>(name)->set(t);
}
template <typename T>
void insert(const std::string & name, T def = 0) {
std::unique_ptr<ICache> up = std::make_unique<Cache<T>>(name);
m_map.insert({name, std::move(up)});
set<T>(name, def);
}
friend std::ostream& operator << (std::ostream & out, const CacheMap & OM) {
out << "OM{";
for (const auto & IO : OM.m_map)
out << IO.first << ": " << *(IO.second.get()) << ", "; // ver1
// out << IO.first << ": " << (IO.second->get()) << ", "; // ver2
out << "}";
return out;
}
};
int main()
{
CacheMap m;
int i= 70000;
m.insert<int>("i", 69999);
m.insert<short>("s", 699);
m.insert<char>("c", 69);
m.set("i", i);
std::cout << m << std::endl;
}威尔螺母的输出
i setting 0 -> 69999
s setting 0 -> 699
c setting 0 -> 69
i setting 69999 -> 70000
OM{c: Cache<c, E>, i: Cache<i, 70000>, s: Cache<s, 699>, }我刚刚发现,为了防止非常糟糕和很难调试错误,我提醒您使用dynamic_cast而不是CacheMap::_get_ptr()中的reintepret_cast。
发布于 2020-01-02 12:06:13
有多种方法,但通常我会实现一个调用虚拟函数的operator<<()。
class ICache {
protected: // so the function is only accessible to derived classes
virtual std::ostream print(std::ostream &out) const = 0; // force derived classes to override
friend std::ostream &operator<<(std::ostream &out, const ICache& c);
};然后将operator<<的单个定义放在一个编译单元中。
// definition of class ICache needs to be visible here
std::ostream &operator<<(std::ostream &out, const ICache& c)
{
return c.print(out);
} 和实现派生类。
// definition of ICache here
template<class T>
class Cache: ICache
{
protected:
std::ostream print(std::ostream &out) const override
{
// output a Cache<T>
return out;
}
};这样做的好处是,每个类都负责输出自己,而不是容器类必须计算出要调用哪个输出函数(以及程序员忘记调用哪个输出函数的机会)。
发布于 2020-01-02 11:52:06
首先,reinterpret_cast是一件危险的事情。在多态上下文中,您主要希望使用dynamic_cast。
您的问题与模板等无关,但更重要的是您希望使operator<<变为虚拟的,这是不可能的,因为它是一个朋友函数。一个简单的解决办法如下:
class ICache {
virtual void print(std::ostream &out) const { // Prefer pure virtual
out << "Never print this\n";
}
friend std::ostream &operator<<(std::ostream &out, const ICache& c) {
c.print(out);
return out;
}
};
template<class T>
class Cache: ICache {
void print(std::ostream &out) const override {
out << "Print this instead\n";
}
};你想做什么就做什么而不做任何演员。
https://stackoverflow.com/questions/59562542
复制相似问题