Streaming into default-constructed object



  • I want to create a static constant by streaming into a default-constructed object. However, the compiler cannot locate the streaming operator, although I provide it:

    @QMap<float, int>& operator<<(QMap<float, int>& map,
    const QPair<float, int>& data)
    {
    (void) map.insert(data.first, data.second);
    return map;
    }

    static const QMap<float, int> s_Map
    = QMap<float, int>() << QPair<float,int>(1.0,0);@

    For contrast, the following code compiles ("works" would be too strong a word)

    @QMap<float, int>& operator<<(QMap<float, int>& map,
    const QPair<float, int>& data)
    {
    (void) map.insert(data.first, data.second);
    return map;
    }

    // Intermediate variable
    static QMap<float, int> s_tempMap;

    static const QMap<float, int> s_Map
    = s_tempMap << QPair<float,int>(1.0,0);@

    Why doesn't the first version compile?

    I'm using the MinGW provided with Qt 4.8 (4.4.0)

    EDIT:
    Completely forgot to post the error message:
    error: no match for 'operator<<' in 'QMap<float, int>() << QPair<float, int>(((const float&)((const float*)(&1.0e+0f))), ((const int&)((const int*)(&0))))'
    candidates are: QMap<float, int>& operator<<(QMap<float, int>&, const QPair<float, int>&)


  • Lifetime Qt Champion

    Hi,
    You could do something like this

    @class FloatIntMap : public QMap<float, int>
    {
    public:
    FloatIntMap() :
    QMap<float, int>()
    {}

    inline FloatIntMap& operator <<(const QPair<float, int>& data)
    {
        insert(data.first, data.second);
        return *this;
    }
    

    };@

    Hope it helps

    EDIT
    Letting my code but removed my suggestion to avoid mistakes for other forum members.



  • The temporary <code>QMap<float, int>()</code> is an r-value, whereas <code>QMap<float, int>& operator<<(QMap<float, int>& map, const QPair<float, int>& data)</code> expects an l-value as its first parameter. The solution with the intermedia variable works as <code>static QMap<float, int> s_tempMap</code> is an l-value.



  • Isn't QMap<float, int>() just a default-constructed map? I've had this construct work in other cases.



  • It indeed is, but it is still an r-value ;-)

    Each expression in C++ is either an l-value or an "r-value":http://thbecker.net/articles/rvalue_references/section_01.html, regardless of its type; and you cannot bind an r-value to a non-const reference - which you do by passing the temporary to operator<<().

    You either add an operator<< taking an r-value reference <code>QMap<float, int>&& map</code> make the first parameter a const reference <code>const QMap<float, int>& map</code> so you can bind an r-value to it, which makes no sense at all, as the map is const then and cannot be modified or you convert your r-value to an l-value by either giving it a name <code>static QMap<float, int> s_tempMap</code> or use an initialization function
    @
    QMap<float, int> initializeMap()
    {
    QMap<float, int> map;
    map << QPair<float, int>(1.0, 0);

    return map;
    

    }

    static const QMap<float, int> s_Map = initializeMap();
    @

    If you want to do it "right" upgrade to Qt5 dev, which has "initializer lists":http://www.stroustrup.com/C++11FAQ.html#init-list for the associative containers like QMap.



  • You have led me to an evil idea. How ugly is this:

    @
    // Regular operator for l-values
    QMap<float, int>& operator<<(QMap<float, int>& map,
    const QPair<float, int>& data)
    {
    (void) map.insert(data.first, data.second);
    return map;
    }

    // Operator for r-values. Creates a copy, appends and returns that
    QMap<float, int> operator<<(const QMap<float, int>& constMap,
    const QPair<float, int>& data)
    {
    QMap<float, int> map = constMap;
    (void) map.insert(data.first, data.second);
    return map;
    }

    // Now this compiles
    static const QMap<float, int> s_Map
    = QMap<float, int>() << QPair<float,int>(1.0,0);
    @

    Qt5 is, unfortunately, not an option.



  • Well, it is as ugly as it more or less strips the const information of your QMap, as people are now allowed to write code like <code>s_Map << QPair<float, int>(2.0, 0)</code> without generating a compiler error (and not modifying the object either).



  • I've decided to use your 2nd suggestion, initialization methods.
    Thanks!



  • I think I would prefer initialization methods as well, as they clearly express what you want to do, initialize an object (and thanks to return value optimization they should save a copy constructor invocation as well).

    Although Qt 4 does not have initializer list support for its containers GCC 4.4 does, which can be used to provide a generic initalization method which should work with any Qt container (or just add the respective constructors to the containers if modifying Qt itself is an option for you).
    @
    template <typename Container,
    typename Value = typename Container::value_type>
    Container initialize(std::initializer_list<Value> values)
    {
    Container container;
    foreach (const Value &value, values)
    container.append(value);

    return container;
    

    }

    template <typename Container,
    typename Key = typename Container::key_type,
    typename Value = typename Container::mapped_type>
    Container initialize(std::initializer_list<QPair<Key, Value>> values)
    {
    Container container;
    foreach (const Value &value, values)
    container.insert(value.first, value.second);

    return container;
    

    }

    const QVector<int> s_Map1 = initialize<QVector<int>>({1, 2, 3});
    const QVector<QPair<int, int>> s_Map2 = initialize<QVector<QPair<int, int>>>({{1,1},
    {2,2},
    {3,3}});
    const QMap<float, int> s_Map3 = initialize<QMap<float, int>>({{1.0, 0},
    {2.0, 0},
    {3.0, 0}});
    @



  • Cool stuff. Unfortunately, I'm stuck at C++2003 for the time being.


Log in to reply
 

Looks like your connection to Qt Forum was lost, please wait while we try to reconnect.