4

Similar to this question, how do I test that a class Impl publicly inherits from a template class BaseTempl (i.e. class Impl : public BaseTempl< ... >{ ... };), without specifying the template arguments?

Different from the aforementioned question, however, I would like the test to still compile (and return false) if the inheritance is not public.

Ideally, the code would allow me to do something like this:

class alpha : public BaseTempl< int >{};

class bravo : BaseTempl< int >{};

class charlie{};

class delta : public BaseTempl< int >, public charlie {};

class echo : public delta {};

int main(){
    publicly_inherits_from < alpha,   BaseTempl > (); // true
    publicly_inherits_from < bravo,   BaseTempl > (); // false
    publicly_inherits_from < charlie, BaseTempl > (); // false
    publicly_inherits_from < delta,   BaseTempl > (); // true
    publicly_inherits_from < echo,    BaseTempl > (); // true
}

The answer from the linked question gives the following error when I attempt to compile the above code:

error: ‘BaseTempl<int>’ is an inaccessible base of ‘bravo’
Community
  • 1
  • 1
A Frayed Knot
  • 476
  • 5
  • 20
  • Possible duplicate of [Trait to check if some specialization of template class is base class of specific class](http://stackoverflow.com/questions/25845536/trait-to-check-if-some-specialization-of-template-class-is-base-class-of-specifi) – Tomilov Anatoliy Mar 01 '16 at 14:46

2 Answers2

5

The following SFINAE-based approach appears to produce the expected result. As an extra bonus, the BaseTempl template can take variadic parameters, and not just one parameter:

#include <iostream>

template<typename ...Args> class BaseTempl {};

template<typename T> class inherits_from_basetempl {

public:

    template<typename ...Args>
    static const bool sfinae_param(const BaseTempl<Args...> &a);

    template<typename V=T>
    static constexpr auto is_inherits(int)
        -> decltype(sfinae_param(std::declval<V &>()))
    {
        return true;
    }

    static constexpr bool is_inherits(...)
    {
        return false;
    }

    static const bool value=is_inherits(0);
};

class alpha : public BaseTempl< int >{};

class bravo : BaseTempl< int >{};

class charlie{};

class delta : public BaseTempl< int >, public charlie {};

class echo : public delta {};

int main()
{
    std::cout << inherits_from_basetempl<alpha>::value << std::endl;
    std::cout << inherits_from_basetempl<bravo>::value << std::endl;
    std::cout << inherits_from_basetempl<charlie>::value << std::endl;
    std::cout << inherits_from_basetempl<delta>::value << std::endl;
    std::cout << inherits_from_basetempl<echo>::value << std::endl;
}

Results:

$ gcc --version
gcc (GCC) 5.3.1 20151207 (Red Hat 5.3.1-2)
Copyright (C) 2015 Free Software Foundation, Inc.
This is free software; see the source for copying conditions.  There is NO
warranty; not even for MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.

$ g++ -std=c++14 -o t t.C 2>&1 | less
$ ./t
1
0
0
1
1
Sam Varshavchik
  • 114,536
  • 5
  • 94
  • 148
  • So close to what I was looking for. I'm really bad with this SFINAE stuff. Is there a way to abstract out BaseTempl as a parameter of the inherits_from_basetempl class? – A Frayed Knot Mar 01 '16 at 04:01
  • Nvm. Figured it out. Accepting your answer but adding my modified version for completeness. Thanks! – A Frayed Knot Mar 01 '16 at 04:07
1

The generic answer:

#include <iostream>

template< typename ...Args > class BaseTempl {};

template< typename T, template< typename... > class B >
class inherits_from_templ_base {

    template< typename ... Args > static const bool sfinae_param( const B<Args...> &a );

    template< typename V = T >
    static constexpr decltype( sfinae_param( std::declval< V& >() ) )
    inherits( int ) { return true; }

    static constexpr bool inherits(...) { return false; }

  public:

    static constexpr bool value = inherits( 0 );
};

class alpha : public BaseTempl< int, char, long >{};

class bravo : BaseTempl< int, char >{};

class charlie{};

class delta : public BaseTempl< int >, public charlie {};

class echo : public delta {};

int main()
{
    std::cout << inherits_from_templ_base<alpha,BaseTempl>::value << std::endl;
    std::cout << inherits_from_templ_base<bravo,BaseTempl>::value << std::endl;
    std::cout << inherits_from_templ_base<charlie,BaseTempl>::value << std::endl;
    std::cout << inherits_from_templ_base<delta,BaseTempl>::value << std::endl;
    std::cout << inherits_from_templ_base<echo,BaseTempl>::value << std::endl;
}
A Frayed Knot
  • 476
  • 5
  • 20