In versions of C++11 and above, you can use the following pattern to perform member detection in a simple way:
template <typename MyClass>
auto swap(MyClass &lhs, MyClass &rhs) -> decltype (lhs.swap(rhs), void())
{
std::cout << "Swapping LHS with RHS by calling the member function!\n";
lhs.swap(rhs);
}
That is, by using the late return type, we're allowed to use the names lhs
and rhs
in decltype
, and multiple expressions in decltype
can be chained by using the comma operator. decltype
will fail to typecheck if the included expressions are not valid, but this will not raise a compiler error but instead trigger SFINAE, as it is encountered during type resolution.
Above code works perfect, as long as:
MyClass
is not a CRDT-child-class of our current class, as in this case because of inheritance, the current class is compiled first and therefore does not yet have access to what members are defined in the child class. And this is what this question is about:
How can a(n overloaded template) function be only get defined if another member function does not exist?
Above example, for instance, defines a swap(lhs, rhs)
that wraps a lhs.swap(rhs)
function. However, in cases where a lhs.swap(rhs)
does not exist, I want to create an alternative variant that, instead of calling lhs.swap(rhs)
, calls std::swap(lhs, rhs)
.
Note that I do not want to use ADL lookup here, because that's the point: The idea is to give a swap(lhs, rhs)
function to any class that only (possibly) has a lhs.swap(rhs)
function defined.
How can this be done?
You'll need to use std::enable_if
for that.
Here's an example. For simplicity, I use a different formulation of the same method for detecting existence of a member, and wrap it in a trait has_member_swap
.
template <typename... T> struct make_void { typedef void type; };
template <typename... T> using void_t = typename make_void<T...>::type;
template <typename, typename, typename = void>
struct has_member_swap : std::false_type { };
template <typename T1, typename T2>
struct has_member_swap<T1, T2,
void_t<decltype( std::declval<T1&>().swap(std::declval<T2&>()) )>
> : std::true_type { };
template <typename T>
std::enable_if_t<has_member_swap<T,T>::value> swap(T& lhs, T& rhs) {
lhs.swap(rhs);
}
template <typename T>
std::enable_if_t<!has_member_swap<T,T>::value> swap(T& lhs, T& rhs) {
std::swap(lhs,rhs);
}
std::void_t
is available since c++17.
The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.