С++ рекурсивно вложенные типы и имя впрыска

Я пробовал эту игру без везения, поэтому я здесь пытаюсь.

У меня есть пара классов, каждый из которых определяет член struct foo. Этот тип элемента foo сам может наследовать один из предыдущих классов, следовательно, сам получить тип элемента foo.

Я хочу получить доступ к вложенным типам foo с использованием метапрограммирования шаблонов (см. ниже), но сглаживание имени С++ вводит проблемы, так как верхнее имя типа foo вводится в нижний тип foo, а верхний разрешается, когда я хочу получить доступ к нижнему, скажем, используя A::foo::foo.

Вот пример:

#include <type_traits>

struct A;
struct B;

struct A {
    struct foo;
};

struct B {
    struct foo;
};

struct A::foo : B { };
struct B::foo : A { };

// handy c++11 shorthand
template<class T>
using foo = typename T::foo;

static_assert( std::is_same< foo< foo< A > >, foo< B > >::value, 
               "this should not fail (but it does)" );

static_assert( std::is_same< foo< foo< A > >, foo< A > >::value, 
               "this should fail (but it does not)" );

FYI, я реализую производные функции, foo - тип производной. Вышеупомянутая ситуация происходит, например, с sin/cos.

TL;DR: как мне получить foo<foo<A>> как foo<B>, а не foo<A>?

Спасибо!

Ответ 1

Это не автоматическое решение, но решает проблему. Ваш типы предоставляют typedef базовому классу, отсутствие/наличие этого typedef обнаруживается через SFINAE, и вложенный foo находится либо через базу или через обычный поиск.

Возможно, вы можете автоматизировать has_base, чтобы проверить список известных базы с is_base_of, если вам нужна дополнительная автоматизация.

#include <type_traits>
template <typename T>
struct has_base
{
    typedef char yes[1];
    typedef char no[2];

    template <typename C>
    static yes& test(typename C::base*);

    template <typename>
    static no& test(...);

    static const bool value = sizeof(test<T>(0)) == sizeof(yes);
};

struct A {
    struct foo;
};

struct B {
    struct foo;
};

struct A::foo : B { typedef B base; };
struct B::foo : A { typedef A base; };

template<typename T, bool from_base = has_base<T>::value >
struct foo_impl {
  typedef typename T::base::foo type;
};

template<typename T> 
struct foo_impl<T, false> {
  typedef typename T::foo type;
};

template<typename T>
using foo = typename foo_impl<T>::type;

static_assert( std::is_same< foo< foo<A> >::, foo< B > >::value, 
               "this should not fail (but it does)" );

static_assert( std::is_same< foo< foo< A > >, foo< A > >::value, 
               "this should fail (but it does not)" );
int main()
{

  return 0;
}