How can I check if a member function exists and is not inherited?
I need this to resolve ambiguity for the following example:
A type either has a foo()
or a bar()
member function. Caller
will call
the one that exists for the given type. However, DerivedWithBar
inherits foo()
from BaseWithFoo
but defines its own bar()
. Thus, Caller
does not know which function to call.
I'd need a way to give the non-inherited foo
precedence over the inherited bar()
, but I do not know how to check if a member function is inherited or not.
#include <iostream>
struct BaseWithFoo
{
template <typename T> void foo(T&&){std::cout << "Base::foo" << std::endl;}
};
struct DerivedWithBar : public BaseWithFoo
{
template <typename T> void bar(T&&){std::cout << "DerivedWithBar::bar" << std::endl;}
};
struct DerivedWithFoo : public BaseWithFoo
{
template <typename T> void foo(T&&){std::cout << "DerivedWithFoo::foo" << std::endl;}
};
struct EmptyDerived : public BaseWithFoo {};
struct BaseWithBar
{
template <typename T> void bar(T&&){std::cout << "BaseWithBar::bar" << std::endl;}
};
struct Caller
{
template <typename T>
auto call(T&& x) -> decltype(x.foo(*this), void())
{
x.foo(*this);
}
template <typename T>
auto call(T&& x) -> decltype(x.bar(*this), void())
{
x.bar(*this);
}
};
int main()
{
Caller c;
c.call(BaseWithFoo());
c.call(DerivedWithFoo());
c.call(DerivedWithBar());
c.call(EmptyDerived());
c.call(BaseWithBar());
}
live example
desired output:
Base::foo
DerivedWithFoo::foo
DerivedWithBar::bar
Base::foo
BaseWithBar::bar
I found a way to distinguish between inherited and non-inherited member functions by comparing types of member function pointers.
The following is a partial solution to my full problem ("giving non-inherited member functions precedence over inherited ones"). This will only call non-inherited foo
or non-inherited bar
.
struct Caller
{
template <typename T>
auto call(T&& x) -> decltype(x.foo(*this),
std::enable_if_t<
std::is_same<
decltype(&T::template foo<decltype(*this)>),
void (T::*)(decltype(*this))
>::value
>())
{
x.foo(*this);
}
template <typename T>
auto call(T&& x) -> decltype(x.bar(*this),
std::enable_if_t<
std::is_same<
decltype(&T::template bar<decltype(*this)>),
void (T::*)(decltype(*this))
>::value
>())
{
x.bar(*this);
}
};
live example
With a bunch of template, we can get it as it follows:
#include<iostream>
#include<type_traits>
#include<utility>
struct BaseWithFoo
{
template <typename T> void foo(T&&){std::cout << "Base::foo" << std::endl;}
};
template<typename T>
struct DerivedWithBarT : public T
{
template <typename U> void bar(U&&){std::cout << "DerivedWithBar::bar" << std::endl;}
};
using DerivedWithBar = DerivedWithBarT<BaseWithFoo>;
template<typename T>
struct DerivedWithFooT : public T
{
template <typename U> void foo(U&&){std::cout << "DerivedWithFoo::foo" << std::endl;}
};
using DerivedWithFoo = DerivedWithFooT<BaseWithFoo>;
template<typename T>
struct EmptyDerivedT : public T {};
using EmptyDerived = EmptyDerivedT<BaseWithFoo>;
struct BaseWithBar
{
template <typename T> void bar(T&&){std::cout << "BaseWithBar::bar" << std::endl;}
};
struct EmptyBase {};
template<typename...>
using void_t = void;
template<typename, typename = void_t<>>
struct HasFoo: std::false_type {};
template<typename T, template<typename> class U>
struct HasFoo<U<T>, void_t<decltype(&U<EmptyBase>::template foo<T>)>>: std::true_type {};
template<typename, typename = void_t<>>
struct HasBar: std::false_type {};
template<typename T, template<typename> class U>
struct HasBar<U<T>, void_t<decltype(&U<EmptyBase>::template bar<T>)>>: std::true_type {};
template<typename T>
struct BarOverFoo {
static constexpr bool value = HasBar<T>::value && !HasFoo<T>::value;
};
template<typename T>
struct FooOverBar {
static constexpr bool value = HasFoo<T>::value && !HasBar<T>::value;
};
template<typename T>
struct Both {
static constexpr bool value = HasFoo<T>::value && HasBar<T>::value;
};
template<typename T>
struct None {
static constexpr bool value = !HasFoo<T>::value && !HasBar<T>::value;
};
struct Caller
{
template <typename T>
std::enable_if_t<FooOverBar<T>::value>
call(T&& x)
{
x.foo(*this);
}
template <typename T>
std::enable_if_t<BarOverFoo<T>::value>
call(T&& x)
{
x.bar(*this);
}
template <typename T>
std::enable_if_t<Both<T>::value>
call(T&& x)
{
x.bar(*this);
}
template <typename T>
std::enable_if_t<None<T>::value>
call(T&& x)
{
callInternal(std::forward<T>(x));
}
private:
template <typename T>
auto callInternal(T&& x) -> decltype(x.foo(*this), void())
{
x.foo(*this);
}
template <typename T>
auto callInternal(T&& x) -> decltype(x.bar(*this), void())
{
x.bar(*this);
}
};
int main()
{
Caller c;
c.call(BaseWithFoo());
c.call(DerivedWithFoo());
c.call(DerivedWithBar());
c.call(EmptyDerived());
c.call(BaseWithBar());
}
Adjust the call
methods according with your requirements.
I've set a few defaults I'm not sure are fine.