What is the preferred method of using std::rel_ops to add the full set of relational operators to a class?
This documentation suggests a using namespace std::rel_ops, but this seems to be deeply flawed, as it would mean that including the header for the class implemented in this way would also add full relational operators to all other classes with a defined operator< and operator==, even if that was not desired. This has the potential to change the meaning of code in surprising ways.
As a side note - I have been using Boost.Operators to do this, but I am still curious about the standard library.
The way operator overloads for user defined classes was meant to work is via argument dependent lookup. ADL allows programs and libraries to avoid cluttering up the global namespace with operator overloads, but still allow convenient use of the operators; That is, without explicit namespace qualification, which is not possible to do with the infix operator syntax a + b and would instead require normal function syntax your_namespace::operator+ (a, b).
ADL, however, doesn't just search everywhere for any possible operator overload. ADL is restricted to look only at 'associated' classes and namespaces. The problem with std::rel_ops is that, as specified, this namespace can never be an associated namespace of any class defined outside the standard library, and therefore ADL cannot work with such user defined types.
However, if you're willing to cheat you can make std::rel_ops work.
Associated namespaces are defined in C++11 3.4.2 [basic.lookup.argdep] /2. For our purposes the important fact is that the namespace of which a base class is a member is an associated namespace of the inheriting class, and thus ADL will check those namespaces for appropriate functions.
So, if the following:
#include <utility> // rel_ops
namespace std { namespace rel_ops { struct make_rel_ops_work {}; } }
were to (somehow) find its way into a translation unit, then on supported implementations (see next section) you could then define your own class types like so:
namespace N {
// inherit from make_rel_ops_work so that std::rel_ops is an associated namespace for ADL
struct S : private std::rel_ops::make_rel_ops_work {};
bool operator== (S const &lhs, S const &rhs) { return true; }
bool operator< (S const &lhs, S const &rhs) { return false; }
}
And then ADL would work for your class type and would find the operators in std::rel_ops.
#include "S.h"
#include <functional> // greater
int main()
{
N::S a, b;
a >= b; // okay
std::greater<N::s>()(a, b); // okay
}
Of course adding make_rel_ops_work yourself technically causes the program to have undefined behavior because C++ does not allow user programs to add declarations to std. As an example of how that actually does matter and why, if you do this, you may want to go to the trouble of verifying that your implementation does in fact work properly with this addition, consider:
Above I show a declaration of make_rel_ops_work that follows #include <utility>. One might naively expect that including this here doesn't matter and that as long as the header is included sometime prior to the use of the operator overloads, then ADL will work. The spec of course makes no such guarantee and there are actual implementations where that is not the case.
clang with libc++, due to libc++'s use of inline namespaces, will (IIUC) consider that declaration of make_rel_ops_work to be in a distinct namespace from the namespace containing the <utility> operator overloads unless <utility>'s declaration of std::rel_ops comes first. This is because, technically, std::__1::rel_ops and std::rel_ops are distinct namespaces even if std::__1 is an inline namespace. But if clang sees that the original namespace declaration for rel_ops is in an inline namespace __1, then it will treat a namespace std { namespace rel_ops { declaration as extending std::__1::rel_ops rather than as a new namespace.
I believe this namespace extension behavior is a clang extension rather than specified by C++, so you may not even be able to rely on this in other implementations. In particular gcc does not behave this way, but fortunately libstdc++ doesn't use inline namespaces. If you don't want to rely on this extension then for clang/libc++ you can write:
#include <__config>
_LIBCPP_BEGIN_NAMESPACE_STD
namespace rel_ops { struct make_rel_ops_work {}; }
_LIBCPP_END_NAMESPACE_STD
but obviously then you'll need implementations for other libraries you use. My simpler declaration of make_rel_ops_work works for clang3.2/libc++, gcc4.7.3/libstdc++, and VS2012.
I think that the preferred technique is not to use std::rel_ops at
all. The technique used in boost::operator (link) seems to be the usual
solution.
Example:
#include "boost/operators.hpp"
class SomeClass : private boost::equivalent<SomeClass>, boost::totally_ordered<SomeClass>
{
public:
bool operator<(const SomeClass &rhs) const
{
return someNumber < rhs.someNumber;
}
private:
int someNumber;
};
int main()
{
SomeClass a, b;
a < b;
a > b;
a <= b;
a >= b;
a == b;
a != b;
}
The problem with adding the rel_ops namespace, regardless of whether you do it with a manual using namespace rel_ops; or whether you do it automatically as described in the answer by #bames53 is that adding the namespace can have unanticipated side effects on portions of your code. I found this myself just recently as I had been using the #bames53 solution for some time, but when I changed one of my container based operations to use a reverse_iterator instead of an iterator (within a multimap but I suspect it would be the same for any of the standard containers), suddenly I was getting compile errors when using != to compare two iterators. Ultimately I tracked it down to the fact that the code included the rel_ops namespace which was interfering with how reverse_iterators are defined.
Using boost would be a way to solve it, but as mentioned by #Tom not everyone is willing to use boost, myself included. So I implemented my own class to solve the problem, which I suspect is also how boost does it, but I didn't check the boost libraries to see.
Specifically, I have the following structure defined:
template <class T>
struct add_rel_ops {
inline bool operator!=(const T& t) const noexcept {
const T* self = static_cast<const T*>(this);
return !(*self == t);
}
inline bool operator<=(const T& t) const noexcept {
const T* self = static_cast<const T*>(this);
return (*self < t || *self == t);
}
inline bool operator>(const T& t) const noexcept {
const T* self = static_cast<const T*>(this);
return (!(*self == t) && !(*self < t));
}
inline bool operator>=(const T& t) const noexcept {
const T* self = static_cast<const T*>(this);
return !(*self < t);
}
};
To use this, when you define your class, say MyClass, you can inherit from this one to add the "missing" operators. Of course you need to define the == and < operators within MyClass (not shown below).
class MyClass : public add_rel_ops<MyClass> {
...stuff...
};
It is important that you include MyClass as the template argument. If you were to include a different class, say MyOtherClass, the static_cast would be almost certain to give you problems.
Note that my solution is assuming that the == and < operators are defined as const noexcept which is one of the requirements of my personal coding standards. If your standards are different, you would need to modify add_rel_ops accordingly.
In addition, if you are bothered by the use of static_cast, you can change them to be a dynamic_cast by adding
virtual ~add_rel_ops() noexcept = default;
to the add_rel_ops class in order to make it a virtual class. Of course, that will also force MyClass to be a virtual class which is why I do not take that approach.
It's not the nicest, but you can use using namespace std::rel_ops as an implementation detail for implementing the comparison operators on your type. For example:
template <typename T>
struct MyType
{
T value;
friend bool operator<(MyType const& lhs, MyType const& rhs)
{
// The type must define `operator<`; std::rel_ops doesn't do that
return lhs.value < rhs.value;
}
friend bool operator<=(MyType const& lhs, MyType const& rhs)
{
using namespace std::rel_ops;
return lhs.value <= rhs.value;
}
// ... all the other comparison operators
};
By using using namespace std::rel_ops;, we allow ADL to lookup operator<= if it is defined for the type, but fall back onto the one defined in std::rel_ops otherwise.
This is still a pain, though, as you still have to write a function for each of the comparison operators.
Related
std::swap() is used by many std containers (such as std::list and std::vector) during sorting and even assignment.
But the std implementation of swap() is very generalized and rather inefficient for custom types.
Thus efficiency can be gained by overloading std::swap() with a custom type specific implementation. But how can you implement it so it will be used by the std containers?
The right way to overload std::swap's implemention (aka specializing it), is to write it in the same namespace as what you're swapping, so that it can be found via argument-dependent lookup (ADL). One particularly easy thing to do is:
class X
{
// ...
friend void swap(X& a, X& b)
{
using std::swap; // bring in swap for built-in types
swap(a.base1, b.base1);
swap(a.base2, b.base2);
// ...
swap(a.member1, b.member1);
swap(a.member2, b.member2);
// ...
}
};
Attention Mozza314
Here is a simulation of the effects of a generic std::algorithm calling std::swap, and having the user provide their swap in namespace std. As this is an experiment, this simulation uses namespace exp instead of namespace std.
// simulate <algorithm>
#include <cstdio>
namespace exp
{
template <class T>
void
swap(T& x, T& y)
{
printf("generic exp::swap\n");
T tmp = x;
x = y;
y = tmp;
}
template <class T>
void algorithm(T* begin, T* end)
{
if (end-begin >= 2)
exp::swap(begin[0], begin[1]);
}
}
// simulate user code which includes <algorithm>
struct A
{
};
namespace exp
{
void swap(A&, A&)
{
printf("exp::swap(A, A)\n");
}
}
// exercise simulation
int main()
{
A a[2];
exp::algorithm(a, a+2);
}
For me this prints out:
generic exp::swap
If your compiler prints out something different then it is not correctly implementing "two-phase lookup" for templates.
If your compiler is conforming (to any of C++98/03/11), then it will give the same output I show. And in that case exactly what you fear will happen, does happen. And putting your swap into namespace std (exp) did not stop it from happening.
Dave and I are both committee members and have been working this area of the standard for a decade (and not always in agreement with each other). But this issue has been settled for a long time, and we both agree on how it has been settled. Disregard Dave's expert opinion/answer in this area at your own peril.
This issue came to light after C++98 was published. Starting about 2001 Dave and I began to work this area. And this is the modern solution:
// simulate <algorithm>
#include <cstdio>
namespace exp
{
template <class T>
void
swap(T& x, T& y)
{
printf("generic exp::swap\n");
T tmp = x;
x = y;
y = tmp;
}
template <class T>
void algorithm(T* begin, T* end)
{
if (end-begin >= 2)
swap(begin[0], begin[1]);
}
}
// simulate user code which includes <algorithm>
struct A
{
};
void swap(A&, A&)
{
printf("swap(A, A)\n");
}
// exercise simulation
int main()
{
A a[2];
exp::algorithm(a, a+2);
}
Output is:
swap(A, A)
Update
An observation has been made that:
namespace exp
{
template <>
void swap(A&, A&)
{
printf("exp::swap(A, A)\n");
}
}
works! So why not use that?
Consider the case that your A is a class template:
// simulate user code which includes <algorithm>
template <class T>
struct A
{
};
namespace exp
{
template <class T>
void swap(A<T>&, A<T>&)
{
printf("exp::swap(A, A)\n");
}
}
// exercise simulation
int main()
{
A<int> a[2];
exp::algorithm(a, a+2);
}
Now it doesn't work again. :-(
So you could put swap in namespace std and have it work. But you'll need to remember to put swap in A's namespace for the case when you have a template: A<T>. And since both cases will work if you put swap in A's namespace, it is just easier to remember (and to teach others) to just do it that one way.
You're not allowed (by the C++ standard) to overload std::swap, however you are specifically allowed to add template specializations for your own types to the std namespace. E.g.
namespace std
{
template<>
void swap(my_type& lhs, my_type& rhs)
{
// ... blah
}
}
then the usages in the std containers (and anywhere else) will pick your specialization instead of the general one.
Also note that providing a base class implementation of swap isn't good enough for your derived types. E.g. if you have
class Base
{
// ... stuff ...
}
class Derived : public Base
{
// ... stuff ...
}
namespace std
{
template<>
void swap(Base& lha, Base& rhs)
{
// ...
}
}
this will work for Base classes, but if you try to swap two Derived objects it will use the generic version from std because the templated swap is an exact match (and it avoids the problem of only swapping the 'base' parts of your derived objects).
NOTE: I've updated this to remove the wrong bits from my last answer. D'oh! (thanks puetzk and j_random_hacker for pointing it out)
While it's correct that one shouldn't generally add stuff to the std:: namespace, adding template specializations for user-defined types is specifically allowed. Overloading the functions is not. This is a subtle difference :-)
17.4.3.1/1
It is undefined for a C++ program to add declarations or definitions
to namespace std or namespaces with namespace std unless otherwise
specified. A program may add template specializations for any
standard library template to namespace std. Such a specialization
(complete or partial) of a standard library results in undefined
behaviour unless the declaration depends on a user-defined name of
external linkage and unless the template specialization meets the
standard library requirements for the original template.
A specialization of std::swap would look like:
namespace std
{
template<>
void swap(myspace::mytype& a, myspace::mytype& b) { ... }
}
Without the template<> bit it would be an overload, which is undefined, rather than a specialization, which is permitted. #Wilka's suggest approach of changing the default namespace may work with user code (due to Koenig lookup preferring the namespace-less version) but it's not guaranteed to, and in fact isn't really supposed to (the STL implementation ought to use the fully-qualified std::swap).
There is a thread on comp.lang.c++.moderated with a long dicussion of the topic. Most of it is about partial specialization, though (which there's currently no good way to do).
I have a library which expose some sort of container struct in which I want to collect diverse generic type of data which might be from the same namespace of the library as well as data from the std namespace such as array or tuple or pairs.
This container has a print_all method which will invoke operator<< for all the elements in the container. Such an operator is supposed to be provided by the user of the library.
Testing the library, I am using different template parameters for T, but I do not care too much about what is being printed by the print_all method. For the test purpose I care only that an example character is being printed no matter which T is being tested. Actually, I my real code I am using Google Test Framework and the behavior of the method under test is really asserted to be the same for every data type provided.
I tried to provide both a generic version of operator<< and two specific versions of it. You can see them in between the #if directive. None of the two compilation branches compiles correctly, most likely because of some violation of the König lookup rules. But anyway what I want to do should be easily possible somehow. What am I missing?
Here is the example code:
#include <algorithm>
#include <iostream>
#include <vector>
namespace my
{
template<typename DataType>
struct Container
{
void print_all( std::ostream& os ) const
{
std::for_each(std::begin(data),
std::end(data),
[&os](const DataType& value)
{
os << value;
});
}
std::vector<DataType> data;
};
namespace test
{
struct Data
{
std::byte data[4];
};
using Array = std::array<std::byte,4>;
#if 0
template<typename T>
std::ostream& operator<<(std::ostream& os, const T& data)
{
return os << 'X';
}
#else
std::ostream& operator<<(std::ostream& os, const Data& data)
{
return os << 'X';
}
std::ostream& operator<<(std::ostream& os, const Array& data)
{
return os << 'X';
}
#endif
void run()
{
// Test with custom data
{
Container< Data> container;
container.data.resize(1);
container.print_all( std::cout );
}
// Test with std data
{
Container< Array> container;
container.data.resize(1);
container.print_all( std::cout );
}
}
} // namespace test
} // namespace my
int main()
{
my::test::run();
return 0;
}
ADL looks into the namespaces associates with the arguments.
using Array = std::array<std::byte,4>;
this type test::Array is an alias. The namespaces associated with it for the purpose of ADL is std. test is not associated with it. ADL will only look in std. You are not permitted to add operators to std; if you violate that, your program is ill-formed, no diagnostic required. ADL cannot help you, because it only helps people who own the namespace(s) associated with the arguments.
Any << you want to support in std needs to be in namespace my, defined before your print_all function.
Your code for Data looks to me like it works, I assume your question was just poorly written, as it implies os <<Data does not work. If I am wromg amd it doesn't work, it is because of some typo; ADL works fine on structs, but not on aliases. In the future, please include complete error messages when code doesn't work.
I'm developing a set of functions from an environment without not-full but high support for C++11 that are intented to be both callable with a multiple set of parameters but easy to pass to higher-level functions without having to force the user to force template parameter instantiations through casting, etc. So the most obvious solution to create these functions are by creating functors in the following way:
namespace mylib {
struct functor_f
{
return_type_1 operator()(type1, type2) const
{ return something; }
template<class Type3>
return_type_2 operator()(type1, type2, Type3) const
{ return something2; }
};
static auto functor = functor_f();
}
The problem with that approach is that, functor will have a different address per translation unit, which could cause problems depending on how the user will treat the functor. If I remove static, then I have to define functor in a .cpp module, and thus adding an invisible level of indirection for the compiler from other translation units, which I guess can cause an important efficiency penalty, since these functors are meant to be called thousands of times per algorithm (graph computations).
The most bizarre solution I can think of is to just, if I cannot guarantee these "object uniqueness", then I forbid its identification by forbiding taking the address of the object, assuming the user doesn't even know the existence of std::address_of, by something like:
namespace mylib {
namespace _impl {
template<class...>
constexpr bool always_false() { return false; }
}
template<class T = void>
struct non_addressable
{
// Or just make-it private, but I would prefer a cleaner
// compile-time diagnostic.
void operator&() const
{ static_assert(_impl::always_false<T>(), "Don't address me"); }
};
struct functor_f : non_addressable<>
{
return_type_1 operator()(type1, type2) const
{ return something; }
template<class Type3>
return_type_2 operator()(type1, type2, Type3) const
{ return something2; }
};
static auto functor = functor_f();
}
But I don't like it at all and I'm not fully sure about the side-effects of such a design.
There was any pre-C++14 idiom to achieve something like this? (simulating inline variables).
I would like to expose C++ code with a
std::vector<A>
to python. My
class A{};
does not have a comparison operator implemented. When I try
BOOST_PYTHON_MODULE(libmyvec)
{
using namespace boost::python;
class_<A>("A");
class_<std::vector<A> >("Avec")
.def(boost::python::vector_indexing_suite<std::vector<A> >());
}
I get an error about comparison operators. If I change the definition of A to
class A {
public:
bool operator==(const A& other) {return false;}
bool operator!=(const A& other) {return true;}
};
It works like a charm.
Why do I need to implement these comparison operators? Is there any way to use the vector_indexing_suite without them?
vector_indexing_suite implements a __contains__ member function, which requires the presence of an equality operator. As a consequence, your type must provide such an operator.
The sandbox version of Boost.Python solve this issue by using traits to determine what kind of operations are available on containers. For instance, find will only be provided if the values are equality comparable.
By default, Boost.Python consider all values to be equality comparable and less-than comparable. Since your type does not meet these requirements, you need to specialize the traits to specify what operations it supports:
namespace indexing {
template<>
struct value_traits<A> : public value_traits<int>
{
static bool const equality_comparable = false;
static bool const lessthan_comparable = false;
};
}
This is documented here.
std::swap() is used by many std containers (such as std::list and std::vector) during sorting and even assignment.
But the std implementation of swap() is very generalized and rather inefficient for custom types.
Thus efficiency can be gained by overloading std::swap() with a custom type specific implementation. But how can you implement it so it will be used by the std containers?
The right way to overload std::swap's implemention (aka specializing it), is to write it in the same namespace as what you're swapping, so that it can be found via argument-dependent lookup (ADL). One particularly easy thing to do is:
class X
{
// ...
friend void swap(X& a, X& b)
{
using std::swap; // bring in swap for built-in types
swap(a.base1, b.base1);
swap(a.base2, b.base2);
// ...
swap(a.member1, b.member1);
swap(a.member2, b.member2);
// ...
}
};
Attention Mozza314
Here is a simulation of the effects of a generic std::algorithm calling std::swap, and having the user provide their swap in namespace std. As this is an experiment, this simulation uses namespace exp instead of namespace std.
// simulate <algorithm>
#include <cstdio>
namespace exp
{
template <class T>
void
swap(T& x, T& y)
{
printf("generic exp::swap\n");
T tmp = x;
x = y;
y = tmp;
}
template <class T>
void algorithm(T* begin, T* end)
{
if (end-begin >= 2)
exp::swap(begin[0], begin[1]);
}
}
// simulate user code which includes <algorithm>
struct A
{
};
namespace exp
{
void swap(A&, A&)
{
printf("exp::swap(A, A)\n");
}
}
// exercise simulation
int main()
{
A a[2];
exp::algorithm(a, a+2);
}
For me this prints out:
generic exp::swap
If your compiler prints out something different then it is not correctly implementing "two-phase lookup" for templates.
If your compiler is conforming (to any of C++98/03/11), then it will give the same output I show. And in that case exactly what you fear will happen, does happen. And putting your swap into namespace std (exp) did not stop it from happening.
Dave and I are both committee members and have been working this area of the standard for a decade (and not always in agreement with each other). But this issue has been settled for a long time, and we both agree on how it has been settled. Disregard Dave's expert opinion/answer in this area at your own peril.
This issue came to light after C++98 was published. Starting about 2001 Dave and I began to work this area. And this is the modern solution:
// simulate <algorithm>
#include <cstdio>
namespace exp
{
template <class T>
void
swap(T& x, T& y)
{
printf("generic exp::swap\n");
T tmp = x;
x = y;
y = tmp;
}
template <class T>
void algorithm(T* begin, T* end)
{
if (end-begin >= 2)
swap(begin[0], begin[1]);
}
}
// simulate user code which includes <algorithm>
struct A
{
};
void swap(A&, A&)
{
printf("swap(A, A)\n");
}
// exercise simulation
int main()
{
A a[2];
exp::algorithm(a, a+2);
}
Output is:
swap(A, A)
Update
An observation has been made that:
namespace exp
{
template <>
void swap(A&, A&)
{
printf("exp::swap(A, A)\n");
}
}
works! So why not use that?
Consider the case that your A is a class template:
// simulate user code which includes <algorithm>
template <class T>
struct A
{
};
namespace exp
{
template <class T>
void swap(A<T>&, A<T>&)
{
printf("exp::swap(A, A)\n");
}
}
// exercise simulation
int main()
{
A<int> a[2];
exp::algorithm(a, a+2);
}
Now it doesn't work again. :-(
So you could put swap in namespace std and have it work. But you'll need to remember to put swap in A's namespace for the case when you have a template: A<T>. And since both cases will work if you put swap in A's namespace, it is just easier to remember (and to teach others) to just do it that one way.
You're not allowed (by the C++ standard) to overload std::swap, however you are specifically allowed to add template specializations for your own types to the std namespace. E.g.
namespace std
{
template<>
void swap(my_type& lhs, my_type& rhs)
{
// ... blah
}
}
then the usages in the std containers (and anywhere else) will pick your specialization instead of the general one.
Also note that providing a base class implementation of swap isn't good enough for your derived types. E.g. if you have
class Base
{
// ... stuff ...
}
class Derived : public Base
{
// ... stuff ...
}
namespace std
{
template<>
void swap(Base& lha, Base& rhs)
{
// ...
}
}
this will work for Base classes, but if you try to swap two Derived objects it will use the generic version from std because the templated swap is an exact match (and it avoids the problem of only swapping the 'base' parts of your derived objects).
NOTE: I've updated this to remove the wrong bits from my last answer. D'oh! (thanks puetzk and j_random_hacker for pointing it out)
While it's correct that one shouldn't generally add stuff to the std:: namespace, adding template specializations for user-defined types is specifically allowed. Overloading the functions is not. This is a subtle difference :-)
17.4.3.1/1
It is undefined for a C++ program to add declarations or definitions
to namespace std or namespaces with namespace std unless otherwise
specified. A program may add template specializations for any
standard library template to namespace std. Such a specialization
(complete or partial) of a standard library results in undefined
behaviour unless the declaration depends on a user-defined name of
external linkage and unless the template specialization meets the
standard library requirements for the original template.
A specialization of std::swap would look like:
namespace std
{
template<>
void swap(myspace::mytype& a, myspace::mytype& b) { ... }
}
Without the template<> bit it would be an overload, which is undefined, rather than a specialization, which is permitted. #Wilka's suggest approach of changing the default namespace may work with user code (due to Koenig lookup preferring the namespace-less version) but it's not guaranteed to, and in fact isn't really supposed to (the STL implementation ought to use the fully-qualified std::swap).
There is a thread on comp.lang.c++.moderated with a long dicussion of the topic. Most of it is about partial specialization, though (which there's currently no good way to do).