Является ли typedef автоматически унаследованным в классе С++?

Раньше я полагал, что "typedef" не наследуется автоматически. Но привязка кода ниже предлагает что-то другое.

#include <iostream>
#include <type_traits>

struct A
{
    typedef int X;
};

struct A_ 
{
    typedef char X;
};

struct B : A {};
struct B_ : A, A_ {};

template< typename ... Ts >
using void_t = void;

template< typename T, typename = void >
struct has_typedef_X : std::false_type {};

template< typename T >
struct has_typedef_X< T, void_t<typename T::X> > : std::true_type {};

int main()
{
    std::cout << std::boolalpha;
    std::cout << has_typedef_X<A>::value << std::endl;
    std::cout << has_typedef_X<A_>::value << std::endl;
    std::cout << has_typedef_X<B>::value << std::endl;
    std::cout << has_typedef_X<B_>::value << std::endl;
    return 0;
}

Результат: true true true false. Но, с моей точки зрения, "has_typedef_X<B>::value", дающее "истина", подразумевает, что в структуре B X "typedef'ed".

Итак, если кто-нибудь может объяснить эту проблему или исправить меня?

Онлайн-версия доступна на http://melpon.org/wandbox/permlink/iwZ6eZ3PoBPgyFBj [исправлено URL]

Ответ 1

Вложенные имена типов (то есть типы членов) родителей видны в области производного класса и доступны до тех пор, пока спецификатор доступа не является закрытым. Если существует несколько типов с одинаковым именем из разных базовых классов, то неквалифицированное имя является неоднозначным.

Наиболее подходящие стандартные цитаты, которые я нашел в этом отношении:

[class.nested.type] §1

Имена типов подчиняются точно тем же правилам области, что и другие имена. [...]

[class.member.lookup] §9

[Примечание: статический член вложенный тип или перечисляющий , определенный в базовом классе T, можно однозначно найти, даже если объект имеет более одного базового класса подобъектом типа T. [...]

Фактически примером использования этого является стандарт, который указывает, что итераторы стандартных контейнеров наследуют шаблон std::iterator, который не содержит ничего, кроме вложенных имен типов. Вся цель наследования состоит в том, чтобы получить эти вложенные имена типов в итератор. (Этот пример устареет в следующей стандартной версии (С++ 17), где std::iterator предлагается больше не использовать.)

Ответ 2

X имеет значение как для B, так и для B_, проблема с B_ заключается в том, что B_::X неоднозначно, поэтому вывод шаблона не подходит для правды has_typedef, оставляя ложным как единственное матч.

Ответ 3

Так как имя X неоднозначно из-за того, что определено в двух базовых классах, это приводит к отказу шаблона для B_ в has_typedef.

Ваше убеждение, что typedefs не унаследовано, возможно, исходило из некоторых специальностей С++ во время поиска неквалифицированного имени, где базовые классы не учитываются. См. Распространение "typedef" из основанного на производный класс для "шаблона"