首页
学习
活动
专区
圈层
工具
发布
社区首页 >问答首页 >返回类型的自动推导

返回类型的自动推导
EN

Stack Overflow用户
提问于 2020-01-02 11:26:55
回答 3查看 94关注 0票数 0

在阅读了返回类型的C++自动推导C++:模板类的向量之后,我仍然想知道如何对对象执行泛型操作(例如操作符<<重载)。我的代码看起来像

代码语言:javascript
复制
#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行将导致分段冲突。

EN

回答 3

Stack Overflow用户

回答已采纳

发布于 2020-01-02 11:58:35

只需使用一个虚拟函数。将变量从Cache<int>指针类型转换为ICache指针时,会丢失有关它的编译时间信息。信息丢失了。您可以在您的dynamic_cast中使用friend ICache::operator<<处理所有不同的类型..。要正确解析类型信息,请使用virtual函数--即。与每个类绑定的唯一数据。

代码语言:javascript
复制
#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;
}

威尔螺母的输出

代码语言:javascript
复制
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

票数 2
EN

Stack Overflow用户

发布于 2020-01-02 12:06:13

有多种方法,但通常我会实现一个调用虚拟函数的operator<<()

代码语言:javascript
复制
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<<的单个定义放在一个编译单元中。

代码语言:javascript
复制
// definition of class ICache needs to be visible here

std::ostream &operator<<(std::ostream &out, const ICache& c)
{
     return c.print(out);
}   

和实现派生类。

代码语言:javascript
复制
// 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;
    }
};

这样做的好处是,每个类都负责输出自己,而不是容器类必须计算出要调用哪个输出函数(以及程序员忘记调用哪个输出函数的机会)。

票数 2
EN

Stack Overflow用户

发布于 2020-01-02 11:52:06

首先,reinterpret_cast是一件危险的事情。在多态上下文中,您主要希望使用dynamic_cast

您的问题与模板等无关,但更重要的是您希望使operator<<变为虚拟的,这是不可能的,因为它是一个朋友函数。一个简单的解决办法如下:

代码语言:javascript
复制
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";
  }
};

你想做什么就做什么而不做任何演员。

票数 1
EN
页面原文内容由Stack Overflow提供。腾讯云小微IT领域专用引擎提供翻译支持
原文链接:

https://stackoverflow.com/questions/59562542

复制
相关文章

相似问题

领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档