У меня есть класс с именем A
и в этом классе у меня есть итеративный контейнер, который я перебираю, следуя некоторым правилам доступа — порядок, пустота и другие.
Чтобы упростить следующий пример, давайте рассмотрим, что я просто перебираю контейнер, но это не могу быть сделано с помощью встроенный контейнерный итератор.
class A {
public:
class iterator {
public:
// Constructor
iterator() {
}
// Destructor
~iterator() {
}
// Advances the iterator
void operator++() {
// Some accessing policy
}
};
private:
std::vector<int> a;
};
Все работает очень хорошо — и выглядит очень аккуратно — за исключением того, что, когда я объявляю свой итератор, я должен использовать typename
— который я в значительной степени предполагаю, что используется для того, чтобы сказать компилятору, что у меня есть тип, а не сам экземпляр класса.
Вопросы:
почему я должен использовать typename
когда я делаю:
A a;
for (typename A::iterator it(...); it != ...; ++it) {
}
Как обычно определяются итераторы, поскольку векторный итератор не требует typename
тег? Связано ли это с объявлением вектора из определения класса, а не из самого вектора?
std::vector<int> v;
for (std::vector<int>::iterator it(v.begin()); it != v.end(); ++it) {
}
Определены ли итераторы внутри класса контейнера — я полагаю, что он называется составом — или, если нет, как итераторы добавляются в пространство имен класса, как в:
std::vector<int>::iterator it;
1 — почему я должен использовать
typename
когда я делаю: […]
Вам не нужно использовать typename
, typename
требуется устранение неоднозначности внутри шаблон, когда вы используете зависимое, полное имя типа. Это Q&А на StackOverflow проясняет вещи. Это:
A a;
typename a::iterator it; // ERROR!
Является нелегальный C ++. При условии, что A
это не имя параметра шаблона, вы должны просто сделать:
A::iterator it;
Если вы находитесь внутри шаблона и A
имя параметра шаблона, например, в:
template<typename A>
struct X
{
void foo()
{
typename A::iterator it;
// ^^^^^^^^
// This is necessary here!
}
};
Тогда вы должны использовать typename
сказать компилятору, что следует ::
это имя типа.
2 — Как обычно определяются итераторы, поскольку векторный итератор не требует
typename
тег?
Опять же, это не правда, что «векторный итератор не требует typename
тегMsgstr «Если у вас есть явная специализация этого вектора, например:
std::vector<int>::iterator it; // "typename" not required
затем typename
не требуется, так как это не требуется в A::iterator it
, Однако если вы находитесь внутри шаблона, как в следующем случае, это потребуется:
template<typename A>
struct X
{
void foo()
{
typename std::vector<A>::iterator it;
// ^^^^^^^^
// This is necessary here!
}
};
Это потому что std::vector<A>::iterator
Вот полное имя зависимого типа.
3 — Определены ли итераторы внутри класса контейнера — я полагаю, что он называется составом — или, если нет, как итераторы добавляются в пространство имен класса, как в [..]
Это может быть сделано путем определения вложенного класса или просто с использованием псевдонимов типов:
template<typename T>
struct X
{
typedef T* iterator;
iterator begin() { /* ... */ }
// ...
};
X<int>::iterator it; // OK: "typename" not required
template<typename T>
void foo(X<T>& x)
{
typename X<T>::iterator it = x.begin();
// ^^^^^^^^
// This is necessary here!
// ...
}
Есть много проблем с вашим примером кода, так что это может быть ответ, который вы ищете (машет рукой) 🙂 Конечно, если ваши примеры не верны, то все ставки сняты.
Я удивлен, что это сработает вообще. «a» — это переменная, «A» — это класс.
Кроме того, при объявлении переменной конструктором по умолчанию вы не используете завершающие скобки ().
A a;
A::iterator it;
for (A::iterator it; it != ...; ++it) {
}
Кроме того, итераторы определены в классе контейнера. Использование typename необходимо только тогда, когда вы имеете дело с шаблонами и только при доступе к чему-то, что может быть интерпретировано как статический член или класс function / nested или typedef. Это может быть дополнительно объяснено ответом Вот который также дал Энди Проул.
Удачи