Reputation: 38969
I'm trying to mimic the behavior of tie
pre C++11.
pair<int, int> test() {
return make_pair(13, 42);
}
int main() {
int a = 1, b = 2;
pair<int&, int&>(a, b) = test();
cout << a << ' ' << b << endl;
}
This works however if I use make_pair
instead to the pair
constructor a
and b
are not assigned.
Why does the pair
constructor work but not make_pair
?
Upvotes: 3
Views: 1428
Reputation: 38969
In 20.2.2[lib.pairs]8 the standard states that pair
uses "explicit types" while make_pair
's "types are deduced".
This is why the standard defines a constructor for pair
:
template <class T1, class T2>
pair(const T1& x, const T2& y)
If you run your code on a C++03 compiler you will get this error:
non-static reference member
int& std::pair<int&, int&>::first
, can't use default assignment operator
The problem is that pair
uses an implicitly-declared copy assignment operator which is not defined if the pair
:
Has a non-static data member of a reference type
Whether defined by make_pair
or the pair
constructor, the template arguments will define both of the pair
's members as int&
so the implicitly-declared copy assignment operator will not be defined. So this cannot be accomplished with a pair
in C++03.
If using return parameter is undesirable, you can write your own implementation of tie
:
template <class T1, class T2>
struct tie{
T1& first;
T2& second;
tie(T1& x, T2& y) : first(x), second(y) {}
tie<T1, T2>& operator=(const pair<T1, T2>& rhs){
first = rhs.first;
second = rhs.second;
return *this;
}
};
This will allow assignment of a pair
:
tie<int, int>(a, b) = test();
To get the exact C++11 behavior which doesn't require template arguments you'll need to define a function. If tie
is nested in namespace details
the function can be defined as:
template <class T1, class T2>
details::tie<T1, T2> tie(T1& x, T2& y) {
return details::tie<T1, T2>(x, y);
}
This will allow assignment of a pair
just as in C++11:
tie(a, b) = test();
Note that this is still intolerant of using int&
template arguments, so details::tie<int&, int&>
and tie<int&, int&>
will fail just as before.
Upvotes: 3
Reputation: 13988
Actually you can use std::make_pair
. But you need to implement reference_wrapper
class to imitate reference. Examplary (not very polished, but working as expected) c++03 approach:
#include <iostream>
#include <utility>
using namespace std;
template <class T>
struct reference_wrapper {
bool is_const;
T* v;
T const* cv;
reference_wrapper(T& t): v(&t), is_const(false) { }
reference_wrapper(T const& t): cv(&t), is_const(true) { }
reference_wrapper &operator=(reference_wrapper const &rw) {
if (rw.is_const) {
*v = *rw.cv;
} else {
*v = *rw.v;
}
}
};
template <class T>
reference_wrapper<T> ref(T &t) {
return reference_wrapper<T>(t);
}
pair<int, int> test() {
return make_pair(13, 42);
}
int main() {
int a = 1, b = 2;
//pair<int&, int&>(a, b) = test(); // works
make_pair(ref(a), ref(b)) = test(); // now it does work
std::cout << a << ' ' << b << std::endl;
}
Upvotes: 3
Reputation: 16431
make_pair
produces a pair of values, not references. That means it would produce pair<int, int>
in your example and you'd be assigning results of test()
to a temporary variable¹.
You can mimic tie
with the following:
template<typename T, typename U>
std::pair<T&, U&> tie_pair(T& l, U& r)
{
return std::pair<T&, U&>(l, r);
}
¹ this is an unfortunate side-effect of C++03 not having ref-qualifiers. In C++≥11 you can delete operator=
for rvalue this
(in non-std classes) and make such cases a compiler error rather than silent surprising behaviour.
Upvotes: 2