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>&) -
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.