In general, tuple
should be used in generic code.
If you know field 1 is a Number or a Chicken, you shouldn't be using a tuple
. You should be using a struct
with a field called Number
.
If you need tuple-like functionality (as one does), you can simply write as_tie
:
struct SomeType {
int Number;
std::string Chicken;
auto as_tie() { return std::tie(Number, Chicken); }
auto as_tie() const { return std::tie(Number, Chicken); }
};
Now you can access SomeType
as a tuple
of references by typing someInstance.as_tie()
.
This still doesn't give you <
or ==
etc for free. We can do that in one place and reuse it everywhere you use the as_tie
technique:
struct as_tie_ordering {
template<class T>
using enable = std::enable_if_t< std::is_base_of<as_tie_ordering, std::decay_t<T>>, int>;
template<class T, enable<T> =0>
friend bool operator==(T const& lhs, T const& rhs) {
return lhs.as_tie() == rhs.as_tie();
}
template<class T, enable<T> =0>
friend bool operator!=(T const& lhs, T const& rhs) {
return lhs.as_tie() != rhs.as_tie();
}
template<class T, enable<T> =0>
friend bool operator<(T const& lhs, T const& rhs) {
return lhs.as_tie() < rhs.as_tie();
}
template<class T, enable<T> =0>
friend bool operator<=(T const& lhs, T const& rhs) {
return lhs.as_tie() <= rhs.as_tie();
}
template<class T, enable<T> =0>
friend bool operator>=(T const& lhs, T const& rhs) {
return lhs.as_tie() >= rhs.as_tie();
}
template<class T, enable<T> =0>
friend bool operator>(T const& lhs, T const& rhs) {
return lhs.as_tie() > rhs.as_tie();
}
};
which gives us:
struct SomeType:as_tie_ordering {
int Number;
std::string Chicken;
auto as_tie() { return std::tie(Number, Chicken); }
auto as_tie() const { return std::tie(Number, Chicken); }
};
and now
SomeTime a,b;
bool same = (a==b);
works. Note that as_tie_ordering
doesn't use CRTP and is an empty stateless class; this technique uses Koenig lookup to let instances find the operators.
You can also implement an ADL-based get
struct as_tie_get {
template<class T>
using enable = std::enable_if_t< std::is_base_of<as_tie_get, std::decay_t<T>>, int>;
template<std::size_t I, class T,
enable<T> =0
>
friend decltype(auto) get( T&& t ) {
using std::get;
return get<I>( std::forward<T>(t).as_tie() );
}
};
Getting std::tuple_size
to work isn't as easy, sadly.
The enable<T> =0
clauses above should be replaced with class=enable<T>
in MSVC, as their compiler is not C++11 compliant.
You'll note above I use tuple
; but I'm using it generically. I convert my type to a tuple, then use tuple's <
to write my <
. That glue code deals with tie as a generic bundle of types. That is what tuple
is for.
c++11
,c++14
,c++1z
, be specific․ – Gadgetryusing
, instead it uses a constexpr variable. – Chlodwig