15

I have already looked at a couple questions on this, specifically Overloading operator<<: cannot bind lvalue to ‘std::basic_ostream<char>&&’ was very helpful. It let me know that my problem is I'm doing something that c++11 can't deduce the type from.

I think a big part of my problem is that the instantiated class I'm working with, is templated, but originally obtained from a pointer to a non-template base class. This is something I did advised from another stackoverflow.com question about how to put template class objects into an STL container.

My classes:

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;
    }
}

And, the code snippet where the compile error database.cc:530:90: error: cannot bind ‘std::basic_ostream<char>’ lvalue to ‘std::basic_ostream<char>&&’ occurs:

//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;
    }

If my output tries to print i->second, it compiles and runs, and I see the pointer. If I try to output *(i->second), I get the compile error. When single-stepping in gdb, it seems to still know that i->second is of the correct type

(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

I'm hoping that I'm doing something subtly wrong. But, it's more complicated than I seem to be able to figure it out on my own. Anyone else see what thing(s) I've done wrong or incompletely?

Edit:

@PiotrNycz did give a good solution for my proposed problem below. However, despite currently printing values while doing development, the real need for these DbValue<> objects is to have them return a value of the correct type which I can then feed to database operation methods. I should've mentioned that in my original question, that printing is of value, but not the end of my goal.

Community
  • 1
  • 1
cross
  • 285
  • 1
  • 3
  • 10
  • `virtual void *null() { return NULL; } // Needed to make class polymorphic` Instead you can just declare the destructor virtual, which you need to do anyway. `virtual ~DbValueBase() = default;` – bames53 Nov 12 '12 at 16:46
  • @barnes53 Thanks. It meant I had to add a `noexcept (true)` to the template subclass destructor, something to do with `default` I'm sure, but cleaner than an unused method. Thanks. – cross Nov 12 '12 at 16:53

2 Answers2

4

If you want to print object by base pointer - make the ostream operator in base class:

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();
    }
};
PiotrNycz
  • 23,099
  • 7
  • 66
  • 112
  • Thanks. I'm sorry that I can't figure out how to get the compiler to figure out the correct form of DbValue<>::operator<< to call, and sorry too that this means I have to write a function which basically does what I want operator<< to do. But, the above does solve the problem without resorting to the `if dynamic_cast> else dynamic_cast> else ...` I was using in the interim. :-) – cross Nov 12 '12 at 18:40
  • However, I realize that I was solely talking about printing because that's what I'm doing so far. But, I'll also need to convert these objects into the templated type because I need to insert them into the database. Your solution will work for performing operations, but not to returning values. Perhaps I should extend my original question to indicate this. – cross Nov 12 '12 at 18:44
  • @cross This is general approach. try this: `virtual void DbValueBase::insertMe(Database& db) = 0; ` – PiotrNycz Nov 15 '12 at 15:21
3

Although the debugger correctly identifies *(i->second) as being of the type DbValue<std::string>, that determination is made using information that is only available at runtime.

The compiler only knows that it is working with a DbValueBase& and has to generate its code on that basis. Therefore, it can't use the operator<<(std::ostream&, const DbValue<T>&) as that does not accept a DbValueBase or subclass.


For obtaining the contents of a DbValue<> object through a DbValueBase&, you might want to loop into the Visitor design pattern.

Some example code:

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;
};
Eran
  • 387,369
  • 54
  • 702
  • 768
Bart van Ingen Schenau
  • 15,488
  • 4
  • 32
  • 41
  • I'll have to see if this works for my needs. I want to effectively return the value, I was thinking, and this doesn't seem to adjust to that. I'll need to see if I can code up a visitor function to apply the values. When I get a little closer to utilizing the data value objects, I'll have a better idea. Thanks for the pointer/suggestion! – cross Nov 13 '12 at 22:08
  • 1
    Was a bit complicated to figure out how to use a Visitor design pattern in my situation, to apply the values (`typename T`) as arguments to an object, but was able to do it. Just had to build the object into the Visitor during construction, then was able to use it like your example above. This is now the accepted answer. Thanks again! – cross Nov 15 '12 at 14:39