Error: невозможно связать 'std:: basic_ostream <char> lvalue to' std:: basic_ostream <char> &&

Я уже рассмотрел пару вопросов по этому поводу, в частности Оператор перегрузки <: не может связывать lvalue с 'std:: basic_ostream <char> && & & было очень полезно. Это дало мне знать, что моя проблема в том, что я делаю то, что С++ 11 не может выводить тип из.

Я думаю, что большая часть моей проблемы заключается в том, что экземпляр класса, с которым я работаю, является шаблоном, но первоначально получен из указателя на базовый класс без шаблонов. Это то, что я посоветовал из другого вопроса stackoverflow.com о том, как помещать объекты класса шаблона в контейнер STL.

Мои классы:

class DbValueBase {
  protected:
    virtual void *null() { return NULL; }   // Needed to make class polymorphic
};

template <typename T>
class DbValue : public DbValueBase {
  public:
    DbValue(const T&val)  { data = new T(val); }
    ~DbValue() { if (data) delete data; }

    T   *data;

    const T&    dataref() const { return *data; } 

    friend std::ostream& operator<<(std::ostream& out, const DbValue<T>& val)
    {
        out << val.dataref();
        return out;
    }
}

И, фрагмент кода, где происходит ошибка компиляции database.cc:530:90: error: cannot bind ‘std::basic_ostream<char>’ lvalue to ‘std::basic_ostream<char>&&’:

//nb:  typedef std::map<std::string,DbValueBase*>  DbValueMap;
    const CommPoint::DbValueMap&    db_values = cp.value_map();
    for (auto i = db_values.cbegin() ; i != db_values.cend() ; i++) {
        // TODO: Need to implement an ostream operator, and conversion
        // operators, for DbValueBase and DbValue<>
        // TODO: Figure out how to get a templated output operator to
        // work... 
 //     DbValue<std::string> *k = dynamic_cast<DbValue<std::string>*>(i->second);
        std::cerr << "  Database field " << i->first << " should have value " << *(i->second) << endl;
    }

Если мой вывод пытается распечатать i->second, он компилируется и запускается, и я вижу указатель. Если я попытаюсь вывести *(i->second), я получаю ошибку компиляции. При одноэтапном в gdb кажется, что все еще известно, что i->second имеет правильный тип

(gdb) p i->second
$2 = (DbValueBase *) 0x680900
(gdb) p *(i->second)
warning: RTTI symbol not found for class 'DbValue<std::string>'
$3 = warning: RTTI symbol not found for class 'DbValue<std::string>'
{_vptr.DbValueBase = 0x4377e0 <vtable for DbValue<std::string>+16>}
(gdb) quit

Я надеюсь, что я делаю что-то тонко неправильно. Но, это сложнее, чем я, кажется, могу понять это самостоятельно. Кто-нибудь еще видит, что я сделал неправильно или не полностью?

Edit:

@PiotrNycz действительно дал хорошее решение для моей предложенной проблемы ниже. Однако, несмотря на то, что в настоящее время печатаются значения при разработке, настоящая потребность в этих объектах DbValue<> заключается в том, чтобы вернуть их правильному типу, который затем я могу передать методам работы с базой данных. Я должен был упомянуть, что в моем первоначальном вопросе эта печать имеет ценность, но не конец моей цели.

Ответ 1

Хотя отладчик правильно идентифицирует *(i->second) как тип DbValue<std::string>, это определение выполняется с использованием информации, доступной только во время выполнения.

Компилятор знает только, что он работает с DbValueBase& и должен генерировать свой код на этом основании. Поэтому он не может использовать operator<<(std::ostream&, const DbValue<T>&), поскольку он не принимает DbValueBase или подкласс.


Чтобы получить содержимое объекта DbValue<> через DbValueBase&, вам может понадобиться зациклиться на шаблоне проектирования Visitor.

Пример кода:

class Visitor {
public:
    template <typename T>
    void useValue(const T& value);
};

class DbValueBase {
public:
    virtual void visit(Visitor&) = 0;
};

template <class T>
class DbValue : public DbValueBase {
pblic:
    void visit(Visitor& v) {
        v.useValue(m_val);
    }
private:
    T m_val;
};

Ответ 2

Если вы хотите напечатать объект по указателю базы - сделайте оператор ostream в базовом классе:

class DbValueBase {
  protected:
    virtual ~DbValueBase() {}
    virtual void print(std::ostream&) const = 0;
    friend std::ostream& operator << (std::ostream& os, const DbValueBase & obj)
    {
       obj.print(os); return os;
    }
};

template <typename T>
class DbValue : public DbValueBase {
  public:
    void print(std::ostream& os) const 
    {
        out << dataref();
    }
};