Версия для печати темы
Нажмите сюда для просмотра этой темы в оригинальном формате
Форум программистов > C/C++: Общие вопросы > проблема компиляции шаблонных классов


Автор: bsa 26.10.2006, 22:21
Пример, иллюстрирующий проблему:
Код
template <class T>
class Container {
    unsigned pos;
public:
    Container(unsigned p): pos(p) {}
    Container(const Container& value) : pos(value.pos) {}
};

template <class T>
class Segment {
    unsigned pos;
public:
    typedef Container<T> Container;
    Segment(unsigned p) : pos(p) {}
    Container operator[](unsigned index) {
        return Container(pos + index*sizeof(T));
    }
};

template<class T>
struct SomeClass {
    struct SomeStruct{
        int s;
        int a;
    };
    typedef Segment<SomeStruct> SegS;

    SomeClass() {
        SegS::Container s(0);
    }
};

main() {
    SomeClass<int> s;
    return 0;
}
При компиляции g++ ругается:
Цитата
aaa.cpp: In constructor ‘SomeClass<T>::SomeClass()’:
aaa.cpp:29: error: expected `;' before ‘s’
aaa.cpp: In constructor ‘SomeClass<T>::SomeClass() [with T = int]’:
aaa.cpp:36:   instantiated from here
aaa.cpp:29: error: dependent-name ‘Segment<SomeClass<T>::SomeStruct>::Container’ is parsed as a non-type, but instantiation yields a type
aaa.cpp:29: note: say ‘typename Segment<SomeClass<T>::SomeStruct>::Container’ if a type is meant

Виновника нашел - struct SomeStruct. Если ее вынести из SomeClass, то все работает. Но, мне совершенно не нужно выносить эту структуру за пределы класса. И вообще, она у меня приватная. smile
Из подсказки следует, что в строке 29 надо использовать ключевое слово "typename". Попробовал - помогло. Но мне это не нравится, почему компилятор счел Container нетипом (non-type)? И как сделать так, чтобы он считал его типом без использования typename?

Автор: Daevaorn 26.10.2006, 22:25
Цитата(bsa @  26.10.2006,  23:21 Найти цитируемый пост)
Попробовал - помогло. Но мне это не нравится, почему компилятор счел Container нетипом (non-type)? И как сделать так, чтобы он считал его типом без использования typename? 

Это стандарт. Никуда не деться. Компилятор не может быть уверен, что SegS::Container есть всегда тип. Поэтому ему и нужно дополнительное разъяснение.

Автор: bsa 26.10.2006, 22:29
интересно, а с чем он его перепутать может? Больше же вариантов нет!
с другой стороны, почему работает без typename, если вынести определение SomeStruct за пределы класса?

Автор: Daevaorn 26.10.2006, 22:36
Цитата(bsa @  26.10.2006,  23:29 Найти цитируемый пост)
интересно, а с чем он его перепутать может? Больше же вариантов нет!

Можно написать специализацию при которой SegS::Container будет именем переменной.
Цитата(bsa @  26.10.2006,  23:29 Найти цитируемый пост)
с другой стороны, почему работает без typename, если вынести определение SomeStruct за пределы класса? 

Это уже интересней. Надо обратиться к библии. Скорей всего, что-то с резолвингом зависимых имен связано...

Добавлено @ 22:38 
Первое что пришло в голову, это то что SomeStruct ведь тоже при специализции может перестать быть типом...

Автор: bsa 26.10.2006, 22:51
Цитата(Daevaorn @ 26.10.2006,  22:36)
Можно написать специализацию при которой SegS::Container будет именем переменной.

Это как?!? В качестве специализации указать не тип, а переменную?!?

Автор: Daevaorn 26.10.2006, 22:52
Цитата(bsa @  26.10.2006,  23:51 Найти цитируемый пост)
Это как?!? В качестве специализации указать не тип, а переменную?!? 

да

Автор: bsa 27.10.2006, 00:31
ясно

Автор: Earnest 27.10.2006, 07:29
Трехэтажные объявления при работе с шаблонами - обычное дело. Спасают только typedef'ы, скажем: 
Код

typedef typename SegS::Container Conteiner; 

Вопрос, почему компилятор то требует typename, то нет, это скорее всего, его личные тараканы. По моему, всегда должен. 

Автор: UnrealMan 27.10.2006, 09:49
Цитата(Earnest @  27.10.2006,  07:29 Найти цитируемый пост)
Вопрос, почему компилятор то требует typename, то нет, это скорее всего, его личные тараканы. По моему, всегда должен.  

Не, тут всё верно. Смотрите: SomeStruct – это не что иное, как SomeClass<T>::SomeStruct, т.е. зависимый от T тип. Стало быть, Segment<SomeStruct> – это также зависимый от T тип. В таком случае для Segment<SomeClass<T>::SomeStruct>::Container по стандарту полагается прописывать typename. Если же мы выносим SomeStruct за пределы шаблонного класса SomeClass, то теперь SomeStruct становится конкретным (ни от чего не зависящим) типом. Стало быть, Segment<SomeStruct> – это тоже конкретное имя и теперь для обращения Segment<SomeStruct>::Container подсказывать компилятору уже ничего не нужно.

Powered by Invision Power Board (http://www.invisionboard.com)
© Invision Power Services (http://www.invisionpower.com)