可以将文章内容翻译成中文,广告屏蔽插件可能会导致该功能失效(如失效,请关闭广告屏蔽插件后再试):
问题:
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
回答1:
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
回答2:
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.