简体   繁体   中英

C++ define a function iff another function does not exist

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.
  • This cannot be used to do it the other way around: Only define a function implementation if another function does not exist.

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.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM