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.
Related
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.
I have a templated class which looks as follows (assume a using namespace std for brevity):
template <typename Type, typename Compare = less<Type>>
struct weighted_base
{
typedef typename set<pair<Type, double>, set_compare<Type, Compare>> mapped_type;
map<Type, mapped_type, Compare> backing_map;
...
};
where set_compare is defined as:
template <typename Type, typename Compare>
struct set_compare
{
bool operator()(const pair<Type, double>& a,
const pair<Type, double>& b)
{
return Compare(a.first, b.first);
}
};
That is, the map takes keys of type Type to std::set<std::pair<Type, double>> values. This has some problems when I use a method such as:
void insert(const Type& from, const Type& to, double weight)
{
//...
mapped_type& adj_nodes = backing_map[from];
adj_nodes.insert(make_pair(to, weight));
}
The problem is that within the set, when it goes to call set_compare, it has a type of const Type&, not Type. Thus, assuming it's std::less, in this case it will be trying to call less<int>::less(const int& a, const int& b) which fails.
Is there some way of fixing it so that both contains can use (effectively) the same comparison function here?
Compare is the type of a binary functor, so you probably need
return Compare()(a.first, b.first);
where Compare() is a temporary, defualt constructed, Compare instance. For example, if you substitute Compare for std::less<int>,
std::cout << std::boolalpha;
std::cout << std::less<int>(5,50) << "\n"; // Error!
std::cout << std::less<int>()(5,50) << "\n"; // OK
Your error is likely to give you many compiler errors, some of which could send you down the wrong track. I suggest fixing it first, then seeing if the code behaves as you expect it to.
In current C++, the class ostream_iterator was designed like the following:
// excerpted from the standard C++
template<class T, ...>
class ostream_iterator
{
public:
ostream_iterator(ostream_type&);
...
ostream_iterator<T,...>& operator =(const T&);
...
};
To me, this design is suboptimal. Because the user must specify the type T when declaring an ostream_iterator like this: ostream_iterator<int> oi(cout); In fact, cout can take any type of object as its argument, rather than only one type. This is an obvious restriction.
// Below is my own version
// doesn't need any template parameter here
class ostream_iterator
{
public:
ostream_iterator(ostream_type&);
...
// define a template member function which can take any type of argument and output it
template<class T>
ostream_iterator<T,...>& operator =(const T&);
...
};
Now, we can use it as follows:
ostream_iterator oi(cout);
I think it is more generic and more elegant than
ostream_iterator<int> oi(cout);
Am I right?
The simple answer is that iterator have associated types and ostream_iterator conceptually violates the concept of an iterator by requiring a value_type even when it is not necessary. (This is basically #pts's answer)
What you are proposing is related to the idea behind the new "transparent operators", such as the new std::plus<void>. Which consist in having a special instantiation whose member function has a delayed type deduction.
It is also backward compatible because void is not a useful instantiation to begin with. Moreover the void parameter is also the default. For example template<T = void> struct std::plus{...} is the new declaration.
A possible implementation of a transparent ostream_iterator
Going back of std::ostream_iterator, an important test is whether we want to make it work with std::copy as std::ostream_iterator is usually used:
std::vector<int> v = {...};
std::copy(v.begin(), v.end(), std::ostream_iterator<int>(std::cout, " "));
The technology for a transparent std::ostream_iterator is not there yet, because this fails:
std::copy(v.begin(), v.end(), std::ostream_iterator<void>(std::cout, " "));
To make this work, one can explicitly define the void instance. (This completes #CashCow 's answer)
#include<iterator>
namespace std{
template<>
struct ostream_iterator<void> :
std::iterator<std::output_iterator_tag, void, void, void, void>
{
ostream_iterator(std::ostream& os, std::string delim) :
os_(os), delim_(delim)
{}
std::ostream& os_;
std::string delim_;
template<class T> ostream_iterator& operator=(T const& t){
os_ << t << delim_;
return *this;
}
ostream_iterator& operator*(){return *this;}
ostream_iterator& operator++(){return *this;}
ostream_iterator& operator++(int){return *this;}
};
}
Now this works:
std::copy(v.begin(), v.end(), std::ostream_iterator<void>(std::cout, " "));
Moreover, if we convince the standard committee to have a default void parameter (as they did with with std::plus):
template<class T = void, ...> struct ostream_iterator{...}, we could go a step further and omit the parameter altogether:
std::copy(v.begin(), v.end(), std::ostream_iterator<>(std::cout, " "));
The root of the problem and a possible way out
Finally, in my opinion the problem might also be conceptual, in STL one expects an iterator to have a definite value_type associated even if it is not necessary like here. In some sense ostream_iterator violates some concepts of what is an iterator.
So there are two things that are conceptually wrong in this usage: 1) when one copies one expects to know the type of the source (container value_type) and target types 2) one is not copying anything in the first place!. In my opinion there is a double design mistake in this typical usage. There should be a std::send that works with a template shift << operators directly, instead of making = redirect to << as ostream_iterator does.
std::send(v.begin(), v.end(), std::cout); // hypothetical syntax
std::send(v.begin(), v.end(), std::ostream_receiver(std::cout, " ")); // hypothetical syntax
std::send(v.begin(), v.end(), 'some ostream_filter'); // hypothetical syntax
(The last argument should fulfill some kind of Sink concept).
** Using std::accumulate instead and a possible implementation of
std::send **
From a conceptual point of view, sending objects to a stream is more of an "accumulate" operation than a copy operator, so in principle std::accumulate should be a more suitable candidate, besides we don't need "target" iterators for it.
The problem is that std::accumulate wants to make copies of every object that is being accumulated, so this doesn't work:
std::accumulate(e.begin(), e.end(), std::cout,
[](auto& sink, auto const& e){return sink << e;}
); // error std::cout is not copiable
To make it work we need to do some reference_wrapper magic:
std::accumulate(e.begin(), e.end(), std::ref(std::cout),
[](auto& sink, auto const& e){return std::ref(sink.get() << e);}
);
Finally, the code can be simplified by having the equivalent of std::plus for the shift operator, in modern C++ this should look like this IM:
namespace std{
template<class Sink = void, class T = void>
struct put_to{
std::string delim_;
using sink_type = Sink;
using input_type = T;
Sink& operator()(Sink& s, T const& t) const{
return s << t << delim_;
}
};
template<>
struct put_to<void, void>{
std::string delim_;
template<class Sink, class T>
Sink& operator()(Sink& s, T const& t){
return s << t;
}
template<class Sink, class T>
std::reference_wrapper<Sink> operator()(std::reference_wrapper<Sink> s, T const& t){
return s.get() << t << delim_;
}
};
}
Which can be used as:
std::accumulate(e.begin(), e.end(), std::ref(std::cout), std::put_to<>{", "});
Finally we can define:
namespace std{
template<class InputIterator, class Sink>
Sink& send(InputIterator it1, InputIterator it2, Sink& s, std::string delim = ""){
return std::accumulate(it1, it2, std::ref(s), std::put_to<>{delim});
}
}
Which can be used as
std::send(e.begin(), e.end(), std::cout, ", ");
Finally, there is no dilemma about the type of any output_iterator here.
It appears you could be right.
Let's see if we can construct an ostream_iterator that does not need a template argument.
The iterator works by copying values into it, so *iter = x; ++iter;
The iterator cheats by making operator* return itself and ++iter also returning itself without changing any state. The "magic" is in the operator= which performs the output.
The "cout" must be a class member of type ostream*. It needs to be a pointer as iterators must be assignable, thus we assign the member (call it os) to the address of the stream passed in.
So we would overload operator= this way:
template< typename T >
our_ostream_iterator& operator=( const T& t )
{
(*os) << t;
if( delim )
(*os) << delim;
return *this;
}
Note that the templatised operator= should not oveload operator=(our_ostream_iterator const&) which is more specialised than the template.
You would still want a template on the element type so we will call that our_basic_ostream_iterator
ostream_iterator would still remain a template class on its element type. Thus:
template< typename E, typename TR=char_traits<E> >
class our_basic_ostream_iterator : public std::iterator< /*traits here*/ >
{
public:
typedef E element_type;
typedef TR traits_type;
typedef basic_ostream< E, TR > stream_type;
private:
stream_type * os;
const E* delim;
public:
our_basic_ostream_iterator( stream_type s, const E* d = nullptr ) :
os( &s ), delim( d )
{
}
our_basic_ostream_iterator& operator++() { return *this; }
our_basic_ostream_iterator operator++(int) { return *this; }
our_basic_ostream_iterator& operator*() { return *this; }
template< typename T >
our_basic_ostream_iterator& operator=( const T& t ); // as above
};
and then of course
typedef our_basic_ostream_iterator<char> our_ostream_iterator;
typedef our_basic_ostream_iterator<wchar_t> our_wostream_iterator;
The drawback of all of this though is that the above does not conform to all the properties of iterators such that it could be passed to any algorithm / class that requires a forward iterator. Why? Because such an algorithm should be able to invoke iterator_traits to extract the element type and the class above does not contain an element type.
It would lead to compile-time errors in the algorithm that is using your iterator and would potentially be hard to track down the reason why.
I think the reason is that it has other members also. Obviously the entire set of member functions need to be consistent in their behavior for a given set of T and other template arguments.
There's danger in operator < being instantiated for a set of template arguments which is different from what is used to instantiate operator * or operator++
Hence, the individual methods are not template themselves and rather the entire class is a template so ensure uniform T and other template arguments.
Yes, you are right. It would be more flexible as you suggest. However, the way it's designed fits more closely to how STL uses iterators: one iterator type for data type (T).
Edit: I fixed my mistake: I'm using a set and not a vector.
Please consider the following example code:
set<Foo *> set_of_foos;
set_of_foos.insert(new Foo(new Bar("x")));
set_of_foos.insert(new Foo(new Bar("y")));
[...]
// The way a "foo" is found is not important for the example.
bool find_foo(Foo *foo) {
return set_of_foos.end() != set_of_foos.find(foo);
}
Now when I call:
find_foo(new Foo(new Bar("x")));
the function returns false since what I'm looking for can't be found. The reason is obvious to me: The pointers point to different objects since they are allocated both with a new, resulting in different values of the addresses.
But I want to compare the contents of Foo (i.e. "x" in the above example) and not Foo * itself. Using Boost is not an option as well as modifying Foo.
Do I need to loop through each of the Foo * inside set_of_foos or is there a simpler solution? I tried uniquely serializing the contents of each Foo and replace the set<Foo *> with a map<string, Foo *>, but this seems like a very "hacked" solution and not very efficient.
Change your vector to set with your custom comparable function to compare Foo objects.
Should be:
struct ltFoo
{
bool operator()(Foo* f, Foo* s) const
{
return f->value() < s->value();
}
};
set<Foo*, ltFoo> sFoo;
sFoo.insert(new Foo(new Bar("x"));
sFoo.insert(new Foo(new Bar("y"));
if (sFoo.find(new Foo(new Bar("y")) != sFoo.end())
{
//exists
}
else
{
//not exists
}
find_foo(new Foo(new Bar("x"))); does not sound like a good idea - it will most likely (in any scenario) lead to memory leak with that search function.
You could use find_if with a functor:
struct comparator {
Foo* local;
comparator(Foo* local_): local(local_) {}
~comparator() { /* do delete if needed */ }
bool operator()(const Foo* other) { /* compare local with other */ }
};
bool found = vec.end() != std::find_if(vec.begin(), vec.end(), comparator(new Foo(...)));
Do I need to loop through each of the Foo * inside vector_of_foos or is there a simpler solution?
You do need to loop to find what you want, but you can use std::find_if or another "wrapped loop". This is more natural with lambdas in C++0x, but in C++03 I'd just use a regular for loop, possibly wrapped in your own function if you need to do this in more than one place.
Instead of using std::find, use std::find_if and provide your own predicate. This of course relies in you being able to access the member that holds "x" in Foo.
struct FooBar
{
FooBar(Foo* search) : _search(search){}
bool operator(const Foo* ptr)
{
return ptr->{access to member} == _search->{access to member};
}
Foo* _search;
}
vector<Foo*>::iterator it = std::find_if(vec.begin(), vec.end(), FooBar(new Foo(new Bar("x")));
If you can't access the member and you can guarantee that all other members will be the same, you could try a bare memcmp in the above functor rather than "==".
You may consider also using the Boost Ptr container library. It allows having a list of pointers using standard algorithms, find, etc. as if it contained objects, and automatically releasing the memory used by the pointers upon vector deletion.
I had the same question and ended up writing a simple DereferenceCompare class to do the job. I'd be curious to know what others think of this. At the crux of the problem is that the existing answers require the programmer using your set to access it in an unusual way that is prone to leaking memory, i.e. by passing an address of a temporary to std::set::find() or through std::find_if(). What's the point of using a standard container if you're going to access it in a non-standard way? Boost has a good container library that solves this problem. But since transparent comparators were introduced in C++14 you can write a custom comparator that makes std::set::insert() and std::set:find() work as expected without depending on Boost. You could use it as something like std::set< Foo*, DereferenceCompare<Foo, YourFooComparator> > set_of_foos;
#ifndef DereferenceCompare_H
#define DereferenceCompare_H
#include <type_traits>
// Comparator for std containers that dereferences pointer-like arguments.
// Useful for containers of pointers, smart pointers, etc. that require a comparator.
// For example:
// std::set< int*, DereferenceCompare<int> > myset1;
// int myint = 42;
// myset1.insert(&myint);
// myset1.find(&myint) == myset.end(); // false
// myset1.find(myint) == myset.end(); // false
// myset1.find(42) == myset.end(); // false
// myset1.find(24) == myset.end(); // true, 24 is not in the set
// std::set<int*> myset2;
// myset2.insert(&myint); // compiles, but the set will be ordered according to the address of myint rather than its value
// myset2.find(&myint) == myset.end(); // false
// myset2.find(a) == myset.end(); // compilation error
// myset2.find(42) == myset.end(); // compilation error
//
// You can pass a custom comparator as a template argument. It defaults to std::less<T>.
// The type of the custom comparator is accessible as DereferenceCompare::compare.
// For example:
// struct MyStruct { int val; };
// struct MyStructCompare { bool operator() (const MyStruct &lhs, const MyStruct &rhs) const { return lhs.val < rhs.val; } };
// std::set< MyStruct*, DereferenceCompare<MyStruct, MyStructCompare> > myset;
// decltype(myset)::key_compare::compare comparator; // comparator has type MyStructCompare
template< typename T, class Compare = std::less<T> > class DereferenceCompare
{
#if __cplusplus==201402L // C++14
private:
// Less elegant implementation, works with C+=14 and later.
template<typename U> static constexpr auto is_valid_pointer(int) -> decltype(*(std::declval<U>()), bool()) { return std::is_base_of<T, typename std::pointer_traits<U>::element_type>::value || std::is_convertible<typename std::remove_cv<typename std::pointer_traits<U>::element_type>::type, T>::value; }
template<typename U> static constexpr bool is_valid_pointer(...) { return false; }
public:
template<typename U, typename V> typename std::enable_if<is_valid_pointer<U>(0) && is_valid_pointer<V>(0), bool>::type operator() (const U& lhs_ptr, const V& rhs_ptr) const { return _comparator(*lhs_ptr, *rhs_ptr); } // dereference both arguments before comparison
template<typename U, typename V> typename std::enable_if<is_valid_pointer<U>(0) && !is_valid_pointer<V>(0), bool>::type operator() (const U& lhs_ptr, const V& rhs) const { return _comparator(*lhs_ptr, rhs); } // dereference the left hand argument before comparison
template<typename U, typename V> typename std::enable_if<!is_valid_pointer<U>(0) && is_valid_pointer<V>(0), bool>::type operator() (const U& lhs, const V& rhs_ptr) const { return _comparator(lhs, *rhs_ptr); } // dereference the right hand argument before comparison
#elif __cplusplus>201402L // Better implementation, depends on void_t in C++17.
public:
// SFINAE type inherits from std::true_type if its template argument U can be dereferenced, std::false otherwise.
// Its ::value member is true if the type obtained by dereferencing U, i.e. the pointee, is either derived from T or convertible to T.
// Its ::value is false if U cannot be dereferenced, or it the pointee is neither derived from nor convertible to T.
// Example:
// DereferenceCompare<int>::has_dereference; // std::false_type, int cannot be dereferenced
// DereferenceCompare<int>::has_dereference<int>::is_valid_pointee; // false, int cannot be dereferenced
// DereferenceCompare<int>::has_dereference<int*>; // std::true_type, int* can be dereferenced to int
// DereferenceCompare<int>::has_dereference<int*>::is_valid_pointee; // true, dereferencing int* yields int, which is convertible (in fact, the same type as) int
// DereferenceCompare<int>::has_dereference< std::shared_ptr<int> >::is_valid_pointee; // true, the pattern also works with smart pointers
// DereferenceCompare<int>::has_dereference<double*>::is_valid_pointee; // true, double is convertible to int
// struct Base { }; struct Derived : Base { }; DereferenceCompare<Base>::has_dereference<Derived*>::is_valid_pointee; // true, Derived is derived from Base
// DereferenceCompare<int>::has_dereference<Derived*>; // std::true_type, Derived* can be dereferenced to Derived
// DereferenceCompare<int>::has_dereference<Derived*>::is_valid_pointee; // false, cannot convert from Derived to int nor does Derived inherit from int
template< typename, class = std::void_t<> > struct has_dereference : std::false_type { static constexpr bool is_valid_pointee = false; };
template< typename U > struct has_dereference< U, std::void_t<decltype(*(std::declval<U>()))> > : std::true_type { static constexpr bool is_valid_pointee = std::is_base_of<T, typename std::pointer_traits<U>::element_type>::value || std::is_convertible<typename std::remove_cv<typename std::pointer_traits<U>::element_type>::type, T>::value; };
template<typename U, typename V> typename std::enable_if<has_dereference<U>::is_valid_pointee && has_dereference<V>::is_valid_pointee, bool>::type operator() (const U& lhs_ptr, const V& rhs_ptr) const { return _comparator(*lhs_ptr, *rhs_ptr); } // dereference both arguments before comparison
template<typename U, typename V> typename std::enable_if<has_dereference<U>::is_valid_pointee && !has_dereference<V>::is_valid_pointee, bool>::type operator() (const U& lhs_ptr, const V& rhs) const { return _comparator(*lhs_ptr, rhs); } // dereference the left hand argument before comparison
template<typename U, typename V> typename std::enable_if<!has_dereference<U>::is_valid_pointee && has_dereference<V>::is_valid_pointee, bool>::type operator() (const U& lhs, const V& rhs_ptr) const { return _comparator(lhs, *rhs_ptr); } // dereference the right hand argument before comparison
#endif
public:
typedef /* unspecified --> */ int /* <-- unspecified */ is_transparent; // declaration required to enable polymorphic comparisons in std containers
typedef Compare compare; // type of comparator used on dereferenced arguments
private:
Compare _comparator;
};
#endif // DereferenceCompare_H
C++11
If you can make use of C++11 features, then you can also use a lambda expression instead of defining a comparison object,
as shown in the other answers. To make the below example code working, I have defined Bar and Foo from your code as follows:
struct Bar {
Bar(std::string s) : str(s) {}
std::string str;
};
struct Foo {
Foo(Bar* p) : pBar(p) {}
Bar* pBar;
};
If you provide the below lambda expression as key comparison function to the std::set,
then your content (i.e. the strings "x" and "y") is compared instead of the pointers pointing to the content.
Consequently, also the find() works as intended, as shown by the following code:
int main() {
auto comp = [](const Foo* f1, const Foo* f2) { return f1->pBar->str < f2->pBar->str; };
std::set<Foo*, decltype(comp)> set_of_foos(comp);
set_of_foos.emplace(new Foo(new Bar("x")));
set_of_foos.emplace(new Foo(new Bar("y")));
auto it = set_of_foos.find(new Foo(new Bar("x")));
if (it == std::end(set_of_foos))
std::cout << "Element not found!" << std::endl;
else
std::cout << "Element found: " << (*it)->pBar->str << std::endl;
return 0;
}
Output:
Element found: x
Code on Ideone
Note: A std::set only allows unique entries (i.e. keys). Whether entries are unique is decided based on the provided key comparison function.
For the code above this means, that you can only store a single entry with pBar->str == "x", even if Bar or Foo are stored at different adresses.
If you want to store multiple entries with pBar->str == "x" (for example), then you have to use a std::multiset.
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;
}