Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Cannot detect protected base class method with SFINAE

I attempted the following approach using c++ SFINAE and std::declval to find if there exists the proper method in a base class, that can be called from the derived class. The derived class gets the base class from a template parameter. This works for a public method in the base class but not a protected method. Ideally I would prefer to keep the method I'm checking for protected. Here is the simplified code

template <class Base>
class Derived : public Base {

  // SFINAE tester to find existence of foo (only works with public foo methods)
  template <class Type>
  static auto test_foo(int) -> decltype(std::declval<Type>().foo(std::declval<Bar&>()), std::true_  type());
  template <class>
  static auto test_foo(...) -> std::false_type;
  using has_foo = decltype(test_foo<Base>(0));

  // using the has_foo constexpr
  void process_bar(Bar& bar) {
    if constexpr (has_foo{})
      Base::foo(bar);
    ...
  }
};

Is there a way to transform this so that it can access the protected member of the base class while using declval (I think using std::declval<Base> might be making it so that it doesn't have access to the protected members of Base despite inheriting from it). Any help is appreciated, thanks!

like image 592
Gemini Em Avatar asked Aug 10 '26 23:08

Gemini Em


1 Answers

If I understand correctly, you want to check the existence of a foo() method receiving a Bar object.

You can try inheriting from the Base template class and from another class (With_foo, in the following example) with a public foo() method. In case of collision, also the Base class has a foo() method (private, protected or public), otherwise only the With_foo class has the method.

Should be enough the following class

template <typename Base>
class Check_foo {

  private: 

    struct With_foo
    { void foo (Bar &) { } };

    template <typename T>
    struct Derived : public With_foo, T 
    { };

    template <typename T,
              typename = decltype(std::declval<T>().foo(std::declval<Bar&>()))>
    static auto test_foo (int) -> std::false_type;

    template <typename...>
    static auto test_foo (long) -> std::true_type;

  public:

    static constexpr bool value = decltype(test_foo<Derived<Base>>(0))::value;
};

The following is a full compiling example


#include <type_traits>

struct Bar 
{ };

class Base_1
{ public: void foo (Bar &) { } };

class Base_2
{ protected: void foo (Bar &) { } };

class Base_3
{ private: void foo (Bar &) { } };

class Base_4
{ };

template <typename Base>
class Check_foo {

  private: 

    struct With_foo
    { void foo (Bar &) { } };

    template <typename T>
    struct Derived : public With_foo, T 
    { };

    template <typename T,
              typename = decltype(std::declval<T>().foo(std::declval<Bar&>()))>
    static auto test_foo (int) -> std::false_type;

    template <typename...>
    static auto test_foo (long) -> std::true_type;

  public:

    static constexpr bool value = decltype(test_foo<Derived<Base>>(0))::value;
};

int main ()
{
  static_assert(    Check_foo<Base_1>::value);
  static_assert(    Check_foo<Base_2>::value);
  static_assert(    Check_foo<Base_3>::value);
  static_assert(not Check_foo<Base_4>::value);
}
like image 127
max66 Avatar answered Aug 12 '26 14:08

max66