Today I tried recompiling a fairly large codebase using GCC
4.6 and -std=c++0x (and also -std=gnu++0x). Due to a couple
of small bugs I was using 4.6.0 plus two patches that will
be
included in the 4.6.1 release. For the purpose of this
exercise, GCC 4.6 is pretty close to implementing the C++0x
rules in the FDIS that was voted for at last week's
committee meeting (there are several C++0x features such as
inheriting constructors and ref-qualifiers for member
functions which are not implemented in GCC yet, but those
features are new in C++0x and so aren't used in C++03
codebases.)
The results were very positive. Apart from adding a few
missing headers (which should have been there anyway)
only a couple of
changes needed to make the C++03 code also valid for
C++0x...
Narrowing conversions
I had to change some initializers to avoid narrowing
conversion (such as double to int, int to char) which are
not
allowed in C++0x. e.g
struct S {
int i;
};
S s = { 0.0 }; // should be { 0 }
for (int i=0; i < 10; ++i) {
char s[2] = { '0'+i }; // should be { char('0'+i) }
}
make_pair<A,B>
The definition of the function template
std::make_pair
has changed in C++0x, but I was
surprised to find this caused a problem. In C++03 it's very
simple:
template<typename T1, typename T2>
std::pair<T1,T2>
make_pair(T1 x, T2 y);
The function deduces the types of its arguments and returns
a
pair
with those types. This saves you from
explicitly specifying the types, as shown by this example in
the standard:
In place of:
return pair<int, double>(5,
3.1415926);// explicit types
a C++ program may contain:
return make_pair(5, 3.1415926);// types are deduced
Simple, eh? Using
make_pair is convenient, but
that's all it is, a convenience
function to save a bit of typing. It therefore makes no
sense to explicitly specify the types:
return make_pair<int, double>(5,
3.1415926);// explicit types
This requires typing five more characters ("make_") than
just constructing a pair directly. It's pointless.
It takes the convenience function and then uses it
inconveniently. It's like putting a stepladder in front of a
high shelf, then standing next to the ladder and reaching
over it. You're doing it wrong!
Yet I found a few cases where that's exactly what had
been
written. And other members of the BSI C++ panel have
reported that it's used in their codebases too.
Why does it matter? Well in C++0x the definition has
changed:
template<typename T1, typename T2>
std::pair<V1,V2>
make_pair(T1&& x, T2&& y);
The template arguments of the returned
pair are not
the same as the template arguments of
make_pair
(they might be the same in some cases, but all.) As you may
be able to guess from the
&& decoration,
this was
done to support move semantics, so that the elements of the
returned
pair can be move constructed (instead of
copy constructed) when the arguments are rvalues (i.e.
temporaries.)
This means that if you disable argument deduction by
providing an explicit template argument list in C++0x, then
the function can only be called if the function arguments
are compatible with the explicit template argument types:
only rvalues arguments will be accepted if you call
make_pair<T1,T2> and only lvalues will be
accepted if you call make_pair<T1&,T2&>
because lvalues can't bind to rvalue-references and
rvalues can't bind to non-const lvalue references.
The solution? Don't call make_pair with
explicit
template arguments.
To be honest, there is one valid reason to give an
explicit
template argument list, which is if you want one type to be
explicitly specified and one to be deduced e.g.
return make_pair<const long>(0, x);
That could be useful to insert into a std::map.
If you really want the old C++03 semantics of
make_pair then you can write your own, as it's
really trivial. Whereas the semantics of the new C++0x
version are subtler and harder to get right, so I think it's
right that the standard library provides the complicated one
and that you have to define the simple one yourself (if you
even need it.)