operator< redefinition on a std::pair<T1,T2> in class template - c++

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>>;

Related

unexpected error in unordered_map iterator comparison

I have a simple map std::map<string, my_namespace::MyType>, I am using c++11 so I replaced it with unordered_map for performance reasons. I got the following error when comparing an iterator with end().
auto cit = str_map_.find(str);
if (cit != str_map_.end()) {
...
}
In instantiation of 'bool my_namespace::operator!=(const T1&, const T2&) [with T1 = std::__detail::_Node_iterator<std::pair<const std::__cxx11::basic_string, my_namespace::MyType, false, true>; T2 = std::__detail::_Node_iterator<std::pair\ <const std::__cxx11::basic_string, my_namespace::MyType, false, true>]': no matching function ...
I debugged it down to my rather creative comparison operators for my_namespace::MyType:
template <class T>
struct MyType {
T* mt_;
};
struct MyTempClass {
std::string mtc_;
static int Compare(MyType<MyTempClass> const& lhs, MyType<MyTempClass> const& rhs) {
return lhs.mt_->mtc_.compare(rhs.mt_->mtc_);
}
static int Compare(std::string const& lhs, MyType<MyTempClass> const& rhs) {
return lhs.compare(rhs.mt_->mtc_);
}
static int Compare(MyType<MyTempClass> const& lhs, std::string const& rhs) {
return lhs.mt_->mtc_.compare(rhs);
}
};
template <class T1, class T2>
bool operator !=(T1 const& lhs, T2 const& rhs) {
int res = MyTempClass::Compare(lhs, rhs);
return (res != 0);
}
template <class T1, class T2>
bool operator ==(T1 const& lhs, T2 const& rhs) {
int res = MyTempClass::Compare(lhs, rhs);
return (res != 0);
}
static std::unordered_map<std::string, MyType<MyTempClass>> my_map;
But I am still puzzled why it did happen: the same code works fine with a plain map, and values type should not be involved in iterator comparisons?
You defined an operator!= overload that takes any type as an argument. That overload is in the same namespace as the type MyType. Therefore, it can potentially be found via ADL.
As the error message indicates, the std::unordered_map iterator used by the standard library is a class template specialization, specialized on the std::unordered_map template arguments. As such, when you compare iterators with !=, ADL is performed on the arguments and the namespaces searched by ADL also include the namespaces of type template arguments of the types of the arguments. Therefore, your operator!= overload in the namespace of MyType will also be found and participate in overload resolution.
Assuming you are using libstdc++ as standard library implementation based on the error message, you can have a look at it's implementation of the operator!= for hash table iterators and you will see that it uses a base class for these iterators, and defines the comparison operators for references to the base class objects.
As a consequence, the standard overload for the iterator comparison requires a derived-to-base reference conversion in its arguments, while your overload does not.
Therefore your overload is better and will be chosen to do the cit != str_map_.end() comparison. Your overload tries to pass the arguments MyTempClass::Compare which clearly doesn't work, because these functions don't expect std::unordered_map iterators.
The solution is not to overload operators for pairs of types that do not depend on user-defined types. Restrict your overloads to your own types:
template <class T1, class T2>
bool operator !=(MyType<T1> const& lhs, T2 const& rhs) {
int res = MyTempClass::Compare(lhs, rhs);
return (res != 0);
}
template <class T1, class T2>
bool operator !=(T1 const& lhs, MyType<T2> const& rhs) {
int res = MyTempClass::Compare(lhs, rhs);
return (res != 0);
}
(equivalently for operator==).
As far as I know, it is not forbidden to overload the operators for standard library type pairs, but I also don't think that the standard library is required to account for conflicts this generates as in your code.
With std::map the standard library implementation might have chosen a different way of implementing the iterator comparison, which made it a better fit in overload resolution or avoided that ADL finds your overload by not making the iterator a template specialized on the key/value type.

c++ operator-overloading for a template struct

I have a template struct "point as follows:
template<typename T>
struct point
{
T x, y, z;
template<typename T1>
inline point<T> operator*(const point<T1>& p) const // multiply by another point.
{
return point<T>{this->x*p.x, this->y*p.y, this->z*p.z};
}
template<typename T1>
inline point<T> operator*(const T1& v) const // multiply by constant from right side
{
return point<T>{this->x*v, this->y*v, this->z*v};
}
}
template<typename T1, typename T2>
inline point<T1> operator*(const T2& v, const point<T1>& p) // multiply by a constant from the left side.
{
return point<T1>{p.x*v, p.y*v, p.z*v};
}
The two operator overloading functions that declared as member functions, the first one is supposed to multiply by another point, and the other to multiply a point by a constant from the right side, where the one declared outside the struct is to do the same thing but from the left side.
And now when I go to compile the following code:
point<double> p1{1,2,3};
point<float> p2{1,2,3};
point<double> p3 = p1*p3;
The compiler calls the one declared outside the struct instead of the one declared as a member function, and produces and error:
error: cannot convert ‘point<double>’ to ‘float’ in initialization
return point<T1>{p.x*v, p.y*v, p.z*v};
And that makes sense, because the both arguments are template variables and can be interpreted as point variables. Now the second one can be only a point variable, but the first one can be anything!
To solve this I can go and write several copies of that function, when declaring the first argument once as int, float, double, long double .. et cetera. This works fine for me, but I'm still wondering if there is a better way to deal with this, and if I can write only one copy as above?
Do not unnecessarily overload operators as member functions,
follow the rules described here.
Having operator* for two point instances return the type of the left one does not make sense, in my opinion. Better consider both types in the deduction of the return type, since the function is defined as commutative.
template<typename T>
struct point
{
T x, y;
};
template<typename T1, typename T2>
auto operator*(const point<T1>& lhs, const point<T2>& rhs)
-> point<decltype(std::declval<T1>() * std::declval<T2>())>
{
return {lhs.x*rhs.x, lhs.y*rhs.y};
}
template<typename T1, typename T2>
point<T1> operator*(const point<T1>& p, const T2& v)
{
return {p.x*v, p.y*v};
}
template<typename T1, typename T2>
point<T1> operator*(const T2& v, const point<T1>& p)
{
return p * v;
}
Try to do the same with the overloads for multiplication by a scalar as an exercise.

Problems overloading operator== of template sub class

I've problems overloading operator==, different compiler errors using VC++(2015) and g++ 5.4.0 (--std==c++14). Here's the code (this is just an extract of a more complex situation in my real code base):
#include <vector>
template<typename T>
struct A {
struct B {
std::vector<T> _elements;
// Internal cmp op.
bool operator==(const B &other) {
return _elements == other._elements;
}
};
std::vector<B> _entries;
};
// External cmp op.
template<typename T>
inline bool operator==(typename const A<T>::B &l, typename const A<T>::B & r) {
return l._elements == r._elements;
}
int main() {
A<int>::B b0, b1;
b0.operator==(b1); // a
operator==<int>(b0, b1); // b
b0 == b1; // c
std::vector<A<int>::B> v0, v1;
std::equal(v0.begin(), v0.end(), v1.begin()); // d
v0 == v1; // e
return 0;
}
I do not add the error messages, because I have the german version of VC++ and the g++ errors span over many lines.
VC++ gives an error on (e). I don't understand why, because vector<>::operator== seems to call std::equal internally and (d) compiles fine. Why does this fail?
g++ fails to accept my external operator==(), so totally fails to compile this short code. I have no idea how to write an external operator==() for A<T>::B that works with both compilers.
I haven't tried clang yet.
Many thanks.
There were two errors in your program:
// Internal cmp op.
bool operator==(const B &other) const {
///// <- here
return _elements == other._elements;
}
should be a const member and the keyword const cannot appear just behind the typename:
// External cmp op.
template<typename T>
inline bool operator==(typename A<T>::B const& lhs, typename A<T>::B const& rhs)
///// <- here -> ////
{
return lhs._elements == rhs._elements;
}
Live Example
Note that the placement of const is usually fairly liberal in C++, e.g. you can write both const typename A<T>::B & lhs and typename A<T>::B const& lhs, but the form you chose, typename const A<T>::B & lhs is not allowed.
Also note that you want to write either a member operator== or a non-member operator==, but never both. In your case, because T is not deducible in typename A<T>::B, you have to write the ugly operator==<int>(b0, b1) to select the non-member.
I would remove the non-member template operator== and add to your class template A<T> a non-member
bool operator==(const A &other) const {
return _entries == other._entries;
}
so that you can also compare objects of A<int>. Note that this will call the standard library operator== for std::vector which in turn will call your operator== for B.

Why can't I use make_pair to tie?

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.

Comparing std::tuple (or std::pair) of custom types who has alternative orderings. Is it possible to plug-in a custom less-than / comparison function?

The Problem
I have a custom type A who has natural ordering (having operator<) and multiple alternative orderings (case-sensitive, case-insensitive, etc.). Now I have a std::pair (or std::tuple) consisting (one or more of) A. Here are some examples of types I want to compare: std::pair<A, int>, std::pair<int, A>, std::tuple<A, int, int>, std::tuple<int, A, int>. How can I compare the std::pair (or std::tuple) using the default element-wise comparison implementation, plugging-in my comparison function for A?
The Code
The code below doesn't compile:
#include <utility> // std::pair
#include <tuple> // std::tuple
#include <iostream> // std::cout, std::endl
struct A
{
A(char v) : value(v) {}
char value;
};
// LOCATION-1 (explained in the text below)
int main()
{
std::cout
<< "Testing std::pair of primitive types: "
<< (std::pair<char, int>('A', 1)
<
std::pair<char, int>('a', 0))
<< std::endl;
std::cout
<< "Testing std::tuple of primitive types: "
<< (std::tuple<char, int, double>('A', 1, 1.0)
<
std::tuple<char, int, double>('a', 0, 0.0))
<< std::endl;
// This doesn't compile:
std::cout
<< "Testing std::pair of custom types: "
<< (std::pair<A, int>('A', 1)
<
std::pair<A, int>('a', 0))
<< std::endl;
return 0;
}
It is because operator< isn't defined for struct A. Adding it to LOCATION-1 above would solve the problem:
bool operator<(A const& lhs, A const& rhs)
{
return lhs.value < rhs.value;
}
Now, we have an alternative ordering for struct A:
bool case_insensitive_less_than(A const& lhs, A const& rhs)
{
char const lhs_value_case_insensitive
= ('a' <= lhs.value && lhs.value <= 'z'
? (lhs.value + 0x20)
: lhs.value);
char const rhs_value_case_insensitive
= ('a' <= rhs.value && rhs.value <= 'z'
? (rhs.value + 0x20)
: rhs.value);
return lhs_value_case_insensitive < rhs_value_case_insensitive;
}
Supposed we want to keep the original operator< for struct A (the case-sensitive one), how can we compare std::pair<A, int> with this alternative ordering?
I know that adding a specialized version of operator< for std::pair<A, int> solves the problem:
bool operator<(std::pair<A, int> const& lhs, std::pair<A, int> const& rhs)
{
return (case_insensitive_less_than(lhs.first, rhs.first)
? true
: case_insensitive_less_than(rhs.first, lhs.first)
? false
: (lhs.second < rhs.second));
}
However, I consider this a sub-optimal solution.
Firstly, for std::pair, it is easy to re-implement the element-wise comparison, but for std::tuple it might be complicated (dealing with variadic templates) and error-prone.
Secondly, I can hardly believe that it is the best-practice way to solve the problem: imagine that we have to define a specialized version of operator< for each of the following classes: std::tuple<A, int, int>, std::tuple<int, A, int>, std::tuple<int, int, A>, std::tuple<A, A, int>, ... (It's not even a practical way!)
Re-using the well written built-in operator< for std::tuple and plugging-in my less-than for struct A would be what I want. Is it possible? Thanks in advance!
The easy way would be to manually write compare( tup, tup, f ) that uses f to lexographically compare the elements in the tuples. But that is boring.
// This type wraps a reference of type X&&
// it then overrides == and < with L and E respectively
template<class X, class L, class E>
struct reorder_ref {
using ref = reorder_ref;
X&& x;
friend bool operator<(ref lhs, ref rhs) {
return L{}((X&&) lhs.x, (X&&) rhs.x);
}
friend bool operator==(ref lhs, ref rhs) {
return E{}((X&&) lhs.x, (X&&) rhs.x);
}
// other comparison ops based off `==` and `<` go here
friend bool operator!=(ref lhs, ref rhs){return !(lhs==rhs);}
friend bool operator>(ref lhs, ref rhs){return rhs<lhs;}
friend bool operator<=(ref lhs, ref rhs){return !(lhs>rhs);}
friend bool operator>=(ref lhs, ref rhs){return !(lhs<rhs);}
reorder_ref(X&& x_) : x((X&&) x_) {}
reorder_ref(reorder_ref const&) = default;
};
the above is a reference that changes how we order.
// a type tag, to pass a type to a function:
template<class X>class tag{using type=X;};
// This type takes a less than and equals stateless functors
// and takes as input a tuple, and builds a tuple of reorder_refs
// basically it uses L and E to compare the elements, but otherwise
// uses std::tuple's lexographic comparison code.
template<class L, class E>
struct reorder_tuple {
// indexes trick:
template<class Tuple, class R, size_t... Is>
R operator()(tag<R>, std::index_sequence<Is...>, Tuple const& in) const {
// use indexes trick to do conversion
return R( std::get<Is>(in)... );
}
// forward to the indexes trick above:
template<class... Ts, class R=std::tuple<reorder_ref<Ts const&, L, E>...>>
R operator()(std::tuple<Ts...> const& in) const {
return (*this)(tag<R>{}, std::index_sequence_for<Ts...>{}, in);
}
// pair filter:
template<class... Ts, class R=std::pair<reorder_ref<Ts const&, L, E>...>>
R operator()(std::pair<Ts...> const& in) const {
return (*this)(tag<R>{}, std::index_sequence_for<Ts...>{}, in);
}
};
the above stateless function object takes some new less and equals operations, and maps any tuple to a tuple of reorder_ref<const T, ...>, which change the ordering to follow L and E respectively.
This next type does what std::less<void> does for std::less<T> sort of -- it takes a type-specific stateless ordering function template object, and makes it a type-generic stateless ordering function object:
// This takes a type-specific ordering stateless function type, and turns
// it into a generic ordering function type
template<template<class...> class order>
struct generic_order {
template<class T>
bool operator()(T const& lhs, T const& rhs) const {
return order<T>{}(lhs, rhs);
}
};
so if we have a template<class T>class Z such that Z<T> is an ordering on Ts, the above gives you a universal ordering on anything.
This next one is a favorite of mine. It takes a type T, and orders it based on a mapping to a type U. This is surprisingly useful:
// Suppose there is a type X for which we have an ordering L
// and we have a map O from Y->X. This builds an ordering on
// (Y lhs, Y rhs) -> L( O(lhs), O(rhs) ). We "order" our type
// "by" the projection of our type into another type. For
// a concrete example, imagine we have an "id" structure with a name
// and age field. We can write a function "return s.age;" to
// map our id type into ints (age). If we order by that map,
// then we order the "id" by age.
template<class O, class L = std::less<>>
struct order_by {
template<class T, class U>
bool operator()(T&& t, U&& u) const {
return L{}( O{}((T&&) t), O{}((U&&) u) );
}
};
Now we glue it all together:
// Here is where we build a special order. Suppose we have a template Z<X> that returns
// a stateless order on type X. This takes that ordering, and builds an ordering on
// tuples based on it, using the above code as glue:
template<template<class...>class Less, template<class...>class Equals=std::equal_to>
using tuple_order = order_by< reorder_tuple< generic_order<Less>, generic_order<Equals> > >;
tuple_order does most of the work for us. All we need is to provide it with an element-wise ordering template stateless function object. tuple_order will then produce a tuple ordering functor based on it.
// Here is a concrete use of the above
// my_less is a sorting functiont that sorts everything else the usual way
// but it sorts Foo's backwards
// Here is a toy type. It wraps an int. By default, it sorts in the usual way
struct Foo {
int value = 0;
// usual sort:
friend bool operator<( Foo lhs, Foo rhs ) {
return lhs.value<rhs.value;
}
friend bool operator==( Foo lhs, Foo rhs ) {
return lhs.value==rhs.value;
}
};
template<class T>
struct my_less : std::less<T> {};
// backwards sort:
template<>
struct my_less<Foo> {
bool operator()(Foo const& lhs, Foo const& rhs) const {
return rhs.value < lhs.value;
}
};
using special_order = tuple_order< my_less >;
and bob is your uncle (live example).
special_order can be passed to a std::map or std::set, and it will order any tuples or pairs encountered with my_less replacing the default ordering of the elements.