Is it valid to have a std::pair of references ? In particular, are there issues with the assignment operator ? According to this link, there seems to be no special treatment with operator=, so default assignement operator will not be able to be generated.
I'd like to have a pair<T&, U&> and be able to assign to it another pair (of values or references) and have the pointed-to objects modified.
In C++11 you can use std::pair<std::reference_wrapper<T>, std::reference_wrapper<U>> and the objects of that type will behave exactly as you want.
No, you cannot do this reliably in C++03, because the constructor of pair takes references to T, and creating a reference to a reference is not legal in C++03.
Notice that I said "reliably". Some common compilers still in use (for GCC, I tested GCC4.1, #Charles reported GCC4.4.4) do not allow forming a reference to a reference, but more recently do allow it as they implement reference collapsing (T& is T if T is a reference type). If your code uses such things, you cannot rely on it to work on other compilers until you try it and see.
It sounds like you want to use boost::tuple<>
int a, b;
// on the fly
boost::tie(a, b) = std::make_pair(1, 2);
// as variable
boost::tuple<int&, int&> t = boost::tie(a, b);
t.get<0>() = 1;
t.get<1>() = 2;
I think it would be legal to have a std::pair housing references. std::map uses std::pair with a const type, after all, which can't be assigned to either.
I'd like to have a pair<T&, U&> and be able to assign to it another pair
Assignment won't work, since you cannot reset references. You can, however, copy-initialize such objects.
You are right. You can create a pair of references, but you can't use operator = anymore.
I was thinking along the same lines as you, I think. I wrote the following class to scratch this particular itch:
template <class T1, class T2> struct refpair{
T1& first;
T2& second;
refpair(T1& x, T2& y) : first(x), second(y) {}
template <class U, class V>
refpair<T1,T2>& operator=(const std::pair<U,V> &p){
first=p.first;
second=p.second;
return *this;
}
};
It allows you to do horrible things like:
int main (){
int k,v;
refpair<int,int> p(k,v);
std::map<int,int>m;
m[20]=100;
m[40]=1000;
m[60]=3;
BOOST_FOREACH(p,m){
std::cout << "k, v = " << k << ", " << v << std::endl;
}
return 0;
}
(remember the relevant includes).
The nastiness is of course that the references to k and v that I am assigning to are hidden inside p.
It almost becomes pretty again if you do something like this:
template <class T1,class T2>
refpair<T1,T2> make_refpair (T1& x, T2& y){
return ( refpair<T1,T2>(x,y) );
}
Which allows you to loop like this:
BOOST_FOREACH(make_refpair(k,v),m){
std::cout << "k, v = " << k << ", " << v << std::endl;
}
(All comments are welcome as I am in no way a c++ expert.)
I don't know what is "wrong" with std::pair in C++03 but if I reimplement it naively, I don't have any problem with it, (using the same compiler gcc and clang).
double a = 1.;
double b = 2.;
my::pair<double, double> p1(5., 6.);
my::pair<double&, double&> p2(a, b);
p2 = p1; // a == 5.
So a workaround could be to (1) reimplement pair (in a different namespace), or (2) specialize for std::pair<T&, T&>, or (3) simply use C++11 (where std::pair for refs works out of the box)
(1) Here it is the naive implementation
namespace my{
template<class T1, class T2>
struct pair{
typedef T1 first_type;
typedef T2 second_type;
T1 first;
T2 second;
pair(T1 const& t1, T2 const& t2) : first(t1), second(t2){}
template<class U1, class U2> pair(pair<U1, U2> const& p) : first(p.first), second(p.second){}
template<class U1, class U2>
pair& operator=(const pair<U1, U2>& p){
first = p.first;
second = p.second;
return *this;
}
};
template<class T1, class T2>
pair<T1, T2> make_pair(T1 t1, T2 t2){
return pair<T1, T2>(t1, t2);
}
}
(2) And here it is an specialization of std::pair (some people may complain that I am messing around overloading/specializing with the std namespace, but I think it is ok if it is to extend the capabilities of the class)
namespace std{
template<class T1, class T2>
struct pair<T1&, T2&>{
typedef T1& first_type; /// #c first_type is the first bound type
typedef T2& second_type; /// #c second_type is the second bound type
first_type first;
second_type second;
pair(T1& t1, T2& t2) : first(t1), second(t2){}
template<class U1, class U2> pair(pair<U1, U2> const& p) : first(p.first), second(p.second){}
template<class U1, class U2>
pair& operator=(const pair<U1, U2>& p){
first = p.first;
second = p.second;
return *this;
}
};
}
Maybe I am missing something obvious, I can edit the answer if some obvious flaws, are pointed.
Post c++14, you can do:
int a, b;
auto const p(std::make_pair(std::ref(a), std::ref(b)));
Using std::cref() is also possible.
I ended up solving a similar problem by just building a really simple structure. I didn't even worry about the assignment operator since the default one should work fine.
template<class U, class V>
struct pair
{
pair(U & first, V & second): first(first), second(second) {}
U & first;
V & second;
}
Related
It's actually a general question about interface design, but it's easier for me to just take std::pair as an example:
template <class T1, class T2>
struct pair {
...
pair(const T1& x, const T2& y);
template<class U, class V> pair(U&& x, V&& y);
...
};
So we can see there are two overloads that both take 2 arguments to initialize the 2 members of the pair. My question is, what is the benefit of providing the first one while the second one is available? Are there any types of arguments that can only be passed to the first one but not the second one?
(Let's just put aside the standard library's consideration for backward compatibility for a while, and discuss the interface design as a general question.)
Sample implementation
template<typename T1, typename T2>
struct simple_pair {
simple_pair (T1 const& v1, T2 const& v2) // (1)
: first (v1)
, second (v2)
{ }
template<class U, class V>
simple_pair (U&& v1, V&& v2) // (2)
: first (std::forward<U> (v1))
, second (std::forward<V> (v2))
{ }
T1 first;
T2 second;
};
Even though it might seem superfluous to provide both overload (1) and (2) there are cases where the second isn't usable, and the first one is not only preferred but actually required.
Consider that we'd like to construct some or both of our values while passing them to the constructor of simple_pair, without the first overload we would explicitly have to specify at least one of the types a second time.
T val;
simple_pair<T, U> p1 { {}, {} }; // only (1) is applicable
simple_pair<T, U> p2 { val, {} }; // only (1) is applicable
simple_pair<T, U> p3 { T {}, U {} }; // can use (1) and (2), but this require a lot of typing
Alternative implementation
If we instead had an implemenation using something as the below we could get around the "superfluous" overloads since the compiler would then know what type we'd like to construct in cases where such information is required.
template<typename T1, typename T2>
struct simple_pair {
template<class U = T1, class V = T2>
simple_pair (U&& v1, V&& v2)
: first (std::forward<U> (v1))
, second (std::forward<V> (v2))
{ }
T1 first;
T2 second;
};
T val;
simple_pair<T, U> p1 { {}, {} }; // legal
simple_pair<T, U> p2 { val, {} }; // legal
simple_pair<T, U> p3 { T {}, U {} }; // legal
Why isn't std::pair stated to be implemented using the alternative implementation?
We can only guess, but presumably it's because of backward compatibility and the fact that specifying it the way it currently stands ease1 implementation for library implementors.
By having two separate overload one can easily disable the template<class U, class V> simple_pair (U&&, V&&) overload by conditionally adding it using macros (to see if we are using c++11 (or later)), instead of conditionally opting it out and adding another one.
Further potential reasons
Removing something from the standard is always a delicate thing to do.. following the better safe than sorry idiom; "if it doesn't hurt, leave it in." - #PlasmaHH
Everyone knows that the more lines of code you write, the better programmer you are.. and the better programmer you are; the more you get payed.
1. surely not by much, but heck.. it doesn't hurt being a bit pedantic.. ;-)
I've got the following situation in a class template case:
template<class T1,class T2>
class targetClass{
public:
typedef typename std::pair<T1, T2> ToSortType;
typedef typename std::set<ToSortType> ContainerSort;
void bar(ToSortType a, ToSortType b);
private:
ContainerSort container;
bool operator<(const ToSortType& rhs) const;
}
template<class T1,class T2>
void targetClass<T1,T2>::bar(ToSortType a, ToSortType b){
container.insert(a);
container.insert(b);
}
template <class T1,class T2>
bool targetClass<T1,T2>::operator<(const ToSortType& rhs) const
{
return this->first < rhs.first;
}
In main function something like this:
targetClass<int,T2> anObjectTarget;
T2 a;
T2 b;
anObjectTarget.bar(std::make_pair(0,a),std::make_pair(1,b));
Where T2 is a user-defined type which generally does not have a defined operator<
In this particular case, std::set has to compare std::pair<int,T2> by firstly check an operator< (and others, maybe) for int type and then for T2. In this case, the compiler can not find a suitable operator for T2. In the previous snippet then I make a redefinition of the operator concerned but the compiler complains in this way:
/usr/include/c++/7/bits/stl_pair.h:456: error: no match for ‘operator<’ (operand types are ‘const T2’ and ‘const T2’)
|| (!(__y.first < __x.first) && __x.second < __y.second); }
~~~~~~~~~~~^~~~~~~~~~~~
I've never redefined an operator before but looking to the documentation it looks correct to me (but not to the compiler).
The operator you overloaded is a member of targetClass<T1,T2> and takes two ToSortType as parameter. Thats not how an overload of the < operator works. Consider that for instances of class type the follwing two are equivalent:
a < b
a.operator<(b)
ie operators are just syntactic sugar for calling special member functions. The operator you wrote could only be called like
targetClass<T1,T2> t;
T1 a;
T2 b;
t.operator<(a,b);
but what the set tries to call is a < b, ie a.operator(b) and that apparently does not exist (std::pair<T1,T2> can only be comared via < when both T1 and T2 can).
Long story short: You cannot use your operator to compare two instances of ToSortType.
I would not recommend to try to overload the operator< for std::pair<T1,T2>, but rather use a custom type:
template<class T1,class T2>
class targetClass{
public:
struct value_type {
T1 first;
T2 second;
bool operator<(const value_type& other) {
return first < rhs.first;
}
}
using container_type = std::set<value_type>;
void bar(const value_type& a,const value_type& b);
private:
container_type container;
};
If you want to stay with std::pair then you can use the fact that std::set allows you to chose the type of the comparator. However, first I have to explain a bit not to confuse you because the following may appear to be contradicting the above (it does not). The default comparator that set uses is std::less<Key>, that is a type with an operator() that compares two element of type Key, it is something similar (but not exactly) like this:
template <typename Key>
struct less {
bool operator() (const Key& a,const Key& b) const {
return a < b;
}
};
And this is the place where the compiler cannot find a < for your Key type (which is std::pair<T1,T2>). You can use your own comparator:
template <typename T1,typename T2>
struct my_comparator {
bool operator() (const std::pair<T1,T2>& a, const std::pair<T1,T2>& b) const {
return a.first < b.first;
}
};
And then your set is
using container_type = std::set<typename std::pair<T1,T2>,typename my_comparator<T1,T2>>;
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?
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;
}
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();
Live Example
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.
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);
}
http://ideone.com/muAcaG
¹ 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.
Is it possible to return an empty pair from a function? Meaning, follow the rules of the function prototype, but do not have any elements in the pair (e.g. NULL). Understanding that a pair simply exists so I don't know if this is conceptually possible. I have a need to return a pair that is NULL or empty, if that makes any sense.
For example,
pair<int, int> MyClass::someFunction()
{
// do something that means we need to return an empty pair
return NULL; // <--- this does not work obviously
}
Unfortunately, boost is not a possibility for me.
Generally speaking an empty pair doesn't even make sense. Afterall a pair is per definition a container containing two objects.
You could however make something like an empty pair using Boost.Optional. Then you would either use a boost::optional<std::pair<...>> giving you the option of returning either a pair or an empty state or use std::pair<boost::optional<...>, boost::optional<...>> for a pair where either object could be empty.
You can returns pointer... Or use boost::optional<T>. Optional will be better...
boost::optional<std::pair<int, int> > MyClass::someFunction()
{
return boost::optional<std::pair<int, int> >();
}
void f(const MyClass& f)
{
boost::optional<std::pair<int, int> > ret = f.someFunction();
if (!ret) // empty
{
...
}
}
The answer to your questions is easily explained by considering the way the C++ compiler generates code in this case.
The std::pair<int, int> is returned-by-value.
Since MyClass::someFunction() is returning an object by value, the sequence of events is as follows:
The calling function reserves space on the stack for a std::pair<int, int>
The MyClass::someFunction() is called
The right hand side of the return statement is a assignment to the location reserved on the stack earlier. There is an implicit construction of a std::pair<int, int> taking place.
Thus returning a NULL pointer is impossible.
It wouldn't take much to create your own "optional pair" (similar to boost::optional<std::pair<…>>, but with a different interface), e.g.:
template <typename T1, typename T2> struct OptPair : std::pair<T1, T2>
{
typedef std::pair<T1, T2> base_t;
bool contains;
OptPair() : contains(true) {}
explicit OptPair(bool x) : contains(x) {}
OptPair(const T1& x, const T2& y) : base_t(x, y), contains(true) {}
template <class U, class V>
OptPair(const std::pair<U,V> &p) : base_t(p), contains(true) {}
template <class U, class V> OptPair(const OptPair<U,V> &p) : base_t(p), contains(p.contains) {}
// No need to define operator=, as the default will construct an OptPair<T1, T2>
// if necessary, then copy members into *this.
};
template <typename T1, typename T2>
OptPair<T1, T2> makeOptPair() { return OptPair<T1, T2>(); }
template <typename T1, typename T2>
OptPair<T1, T2> makeOptPair(const T1 &x, const T2 &y) {
OptPair<T1, T2> p(true);
p.first = x;
p.second = y;
return p;
}
template <typename OS, typename T1, typename T2>
OS &operator<<(OS &os, const OptPair<T1, T2>& p) {
os << "<OptPair: ";
if (p.contains) os << p.first << ", " << p.second;
else os << "empty";
os << ">";
return os;
}
Then you can use it just like std::pair (and even use it interchangeably with std::pair, assigning values back and forth), but with the added ability to pass an "empty" value back like this:
OptPair<int, int> someFunction()
{
...
return OptPair<int, int>(false);
}
You have to make sure to check the result before using it, like this:
void doStuffWithPair(std::pair<int, int>);
void doStuffWithEmpty();
...
OptPair<int, int> ret = someFunction();
if (ret.contains) doStuffWithPair(ret);
else doStuffWithEmpty();
A pair, by definition, has 2 elements. It cannot have none.
You need something like boost::optional<std::pair<T1,T2>>. Then you can choose to have a pair or not. You can find documentation for boost::optional here.
In definition of pair class in c++ there are two typedefs. what are they for? there are no use of them in the code!
template <class T1, class T2> struct pair
{
typedef T1 first_type;
typedef T2 second_type;
T1 first;
T2 second;
pair() : first(T1()), second(T2()) {}
pair(const T1& x, const T2& y) : first(x), second(y) {}
template <class U, class V>
pair (const pair<U,V> &p) : first(p.first), second(p.second) { }
}
They are just here for you convenience, so you can use them in your code. C++ doesn't have a reflection model , so that's the only way you have "know" what types they are
Suppose you define your own pair
typedef pair MyPair;
Then you can use
MyPair::first_type
MyPair::second_type
for example,
MyPair::first_type my_first(MyPair& pair)
{
return pair.first;
}
Then you won't need to research and replace everywhere in your code , if you change the original definition of MyPair.
It's to allow other pieces of code to declare variables of the types without having direct access to the type parameters (T1 & T2). A similar, less trivial, example are the typedefs in container classes:
vector<int>::iterator curNum;
for(curNum = someVect.begin(); curNum != someVect.end(); curNum++)
; //do stuff
This uses the typedef iterator defined in the vector template to create curNum. It will be somewhat less useful come C++0x's auto keyword:
for(auto curNum = someVect.begin(); curNum != someVect.end(); curNum++)
;
This is so you can refer to the types in your code using e.g. pair<int,string>::first_type myVariable, or if you've typedef'd a particular flavour of the template then MyPair::first_type myVariable.
They are public aliases of the passed-in T1 and T2 types that can be referenced after the object is constructed.