Функция, возвращающая T <A>, не компилируется в определенной ситуации typedef - PullRequest
4 голосов
/ 13 августа 2011

Я не понимаю, почему func3() не может скомпилировать, когда func2() и func4() делают.

  • г ++ 4.1.2 : error: 'B<T>::my_t' has incomplete type
  • VS2008: error C2079: 'B<T>::my_t' uses undefined class 'A'

template <typename T>
struct C {
    T mt_t;
};

template <typename T>
struct B {
    typedef C<T> C_type;
    T my_t;
};

struct Other {};

struct A {
    B<A>             func2();
    B<A>::C_type     func3(); // error: 'B<T>::my_t' has incomplete type
    B<Other>::C_type func4();
};

int main() {}

Ответы [ 5 ]

3 голосов
/ 13 августа 2011

Здесь действуют несколько факторов.


A является неполным

Во-первых, во всех объявлениях функций:

B<A>             func2();
B<A>::C_type     func3();
B<Other>::C_type func4();

A является неполным типом:

[2003: 9.2/2]: Класс считается полностью определенным типом объекта (3.9) (или полным типом) при закрытии } класса .спецификатор .В пределах класса спецификация члена класс считается завершенным в теле функций, аргументах по умолчанию и конструкторе ctor-initializer (включая такие вещи во вложенных классах). В противном случае он считается неполным в своем собственном классе спецификация члена .


B<A> требует создания экземпляра

func2 и func4 в порядке

Во-вторых, возвращаемый функцией тип может быть неполным.Это означает, что возвращаемые типы func2 и func4 хороши в том виде, в каком они есть.

[2003: 8.3.5/6]: [..] Тип параметра или тип возвращаемого значения для функцииопределение не должно быть неполным типом класса (возможно, cv-квалифицированным), если определение функции не вложено в спецификацию члена для этого класса (включая определения во вложенных классах, определенных в классе).

func3 не в порядке

Однако в более сложном примере func3 для использования типа B<A>::C_type необходимо заполнить B<A>. *

И поэтому он также должен быть создан :

[2003: 14.7.1/1]: Если специализация шаблона класса не была явно создана (14.7.2) или явно специализирована (14.7.3), специализация шаблона класса неявно создается, когда на специализацию ссылаются в контексте, который требует полностью определенного типа объекта или когда полнота типа класса влияет насемантика программы.[..]

Но поскольку B<A> содержит член типа A, а A не является полным типом, а [8.3.5/6] требует, чтобы это было, экземплярневерно, B<A> остается неполным ... и программа не сформирована.


* Я еще не нашел цитаты, подтверждающей это, хотя это кажется очевидным.

3 голосов
/ 13 августа 2011

Хорошо, когда вы пытаетесь получить B<A>::C_type, вам нужно создать экземпляр template <typename> B, но вы не можете создать экземпляр шаблона для неполного типа, потому что он содержит объект-член T my_t, который не должен быть неполным - компилятор не знает, что вы хотите получить только для члена typedef, равного B<A>.

В func2 вы используете только B<A> в качестве возвращаемого типа, который может быть неполным - нам не нужно создавать экземпляр B<A>, чтобы разрешить его в качестве возвращаемого типа. Но для доступа к члену (typedef) нам do необходимо создать экземпляр B<A>. Furhtermore, func4 хорошо, потому что Other - полный тип.

Решение простое, просто разрешите typedef вручную и сделайте тип возврата func3 в C<A>.

1 голос
/ 13 августа 2011

Я думаю, что соответствующий абзац взят из главы 2 классов:

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

B<A> func2()  //A is incomplete here, but B<A> isn't instantiated
{
    return B<A>();  //A is complete here
}

B<A>::C_type func3() //A is incomplete, B<A> needs to be instantiated for C_type
{
    return B<A>::C_type(); //OK, A is complete
}
0 голосов
/ 13 августа 2011

Это циклическая ссылка:

Чтобы компилятор мог понять, что такое структура A, он должен понимать, что такое B<A>::C_type (поскольку он содержитчлен, который возвращает объект этого типа).Итак, чтобы понять, что такое B<A>::C_type, компилятор должен понять, что такое B<A>.Но чтобы понять, что такое B<A>, компилятор должен знать, что такое A.

По сути, вы дали компилятору парадокс: чтобы понять, что такое A, высначала нужно знать, что такое A.

0 голосов
/ 13 августа 2011

Вы можете сохранить элемент my_t в template <typename T> struct B в качестве указателя и обрабатывать его создание, уничтожение и копирование в struct B, например:

template <typename T>
struct B
{
    typedef C<T> C_type;
    T* my_t;
};
...