首页 > 解决方案 > 返回类型的自动扣除

问题描述

在阅读了 C++ auto deduction of return typeC++ : Vector of template class之后,我仍然想知道如何对对象进行泛型操作(例如运算符<<重载)。我的代码看起来像

#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根本不会编译,这也是有道理的。

我想做的是在CacheMap运行时自动检测(嗯听起来很糟糕)正确的T,给定一个映射键,以便触发正确reinterpret_cast<>并检索m_cached值。

编辑 1

如果使用 编译g++ -O3,则ver1行会导致分段违规。

标签: c++templatestemplate-meta-programming

解决方案


只需使用虚拟功能。将变量从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;
}

在 Godbolt 上输出

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而不是reintepret_castin CacheMap::_get_ptr()


推荐阅读