Почему компилятор не может различить typedef и non-typedef? - PullRequest
4 голосов
/ 17 декабря 2010

Извините за длинный заголовок.

У меня есть typedef в списке классов:

template <typename T>
class List { 
    // Think of a class Iter_ with ListElem *pCurrentPos and List *pList
    typedef const Iter_ const_iterator; 

    const_iterator cbegin() const;
};

и определение вне класса, но внутри заголовочного файла.

template <typename T>
typename List<T>::const_iterator List<T>::cbegin() const {}

Это выдает ошибку C2373: Redefinition; different type modifiers

Я переписал функцию так:

template <typename T>
const typename List<T>::Iter_ List<T>::cbegin() const {}

и ошибка исчезла; программа компилируется правильно. (обдумайте тот факт, что я ничего не возвращаю в этих примерах; это не имеет отношения к примеру.)

Что компилятор интерпретирует с ошибочной версией, которая препятствует успешной компиляции, чего не делает вторая версия, и как я могу это исправить?

Подробнее код

Я использую VS2008

(более полный) пример кода, который я сейчас программирую:

template <typename T>
class List
{
public:
    // Forward declaration.
    class Iter_;

private:
    /////////////////////////////////////////////////////////
    // ListElem
    /////////////////////////////////////////////////////////
    struct ListElem
    {
        T data;
        // Doubly-linked list.
        ListElem *next;
        ListElem *prev;
    };

    class ListException {};

    ////////////////////////////////////////////////////////
    // List Members
    ////////////////////////////////////////////////////////
    // Point to first elem.
    ListElem *head_;
    // Point to last elem.
    ListElem *tail_;

public:
    //////////////////////////////////////////////////////////
    // Typedefs
    //////////////////////////////////////////////////////////
    typedef       Iter_   iterator;
    typedef const Iter_   const_iterator;

    //////////////////////////////////////////////////////////
    // Iterator class
    //////////////////////////////////////////////////////////
    class Iter_
    {
    public:
        Iter_( ListElem *pCurr, List *pList )
            : pCurr_(pCurr), pList_(pList)
        {       }

        T& operator*()
        {
            if( *this == pList_->end() )
                throw ListException();
            else
                return pCurr_->data;
        }

    private:
        ListElem *pCurr_;
        List     *pList_;
    };

iterator begin();
iterator end();

const_iterator cbegin() const;
const_iterator cend()   const;
};

template <typename T>
List<T>::List()
    : head_(0), tail_(0), size_(0)
{   }

template <typename T>
List<T>::~List()
{
    //this->clear();
}

template <typename T>
List<T>::List( List const& other )
    : size_(other.size_)
{
    //this->clone(other);
}

template <typename T>
List<T>& List<T>::operator=( List const& other )
{
    size_ = other.size_;
    //this->clone(other);
}

// Compiles ok
template <typename T>
typename List<T>::iterator List<T>::begin()
{
    if(!head_)
        head_ = new ListElem();
    return iterator(head_, this);
}

// Compiles ok
template <typename T>
typename List<T>::iterator List<T>::end()
{
    return iterator(tail_, this);
}

// Compiler error
template <typename T>
typename List<T>::const_iterator List<T>::cbegin() const
{
    return const_iterator(head_, this);
}

// Compiles ok
template <typename T>
typename const List<T>::Iter_ List<T>::cend() const
{
    return const_iterator(tail_, this);
}

Ответы [ 7 ]

2 голосов
/ 17 декабря 2010

Ошибка, которую я получаю при создании экземпляра cbegin(), заключается в том, что вы передаете (const) this в конструктор, который принимает неконстантный указатель на List.

В принципе, я сомневаюсь, что эта идея так хорошо работает.

 typedef const Iter_   const_iterator;
1 голос
/ 17 декабря 2010

Давайте попробуем свести это к максимально простому. MSVC 2008 компилирует этот файл?

template <typename T> class L {
public:
    class I {};
    typedef const I C;
    C f() const;
};

template <typename T> typename L<T>::C L<T>::f() const { return C(); }

int main() {
  L<int> x;
  x.f();
  return 0;
}

Если нет, у вас есть небольшая демонстрация ошибки компилятора!

1 голос
/ 17 декабря 2010

Тестируя это на GCC 4.5.0 (MinGW), следующий код компилируется нормально:

template <typename T>
class List { 
  public:
  class Iter_ {};
    // Think of a class Iter_ with ListElem *pCurrentPos and List *pList
    typedef const Iter_ const_iterator; 

    const_iterator cbegin() const;
    const_iterator cend() const;
};
template <typename T>
typename List<T>::const_iterator List<T>::cbegin() const {}
template <typename T>
const typename List<T>::Iter_ List<T>::cend() const {}

Если я изменю последнюю строку на

typename const List<T>::Iter_ List<T>::cend() const {}

не скомпилируется. Марк отлично объяснил это: typename List<T>::Iter - это одна вещь, которую нельзя отделять, вставляя в нее модификаторы случайного типа. Это также отлично работает:

typename List<T>::Iter_ const List<T>::cend() const {}

Поведение GCC имеет для меня смысл, так что я думаю, что это ошибка в компиляторе MSVC.

1 голос
/ 17 декабря 2010

Edit:

Как очень странно. Я даже использовал автоматическое удержание типа, чтобы определенно получить правильный тип возвращаемого значения, и он все равно отклонил мой код.

template <typename T>
decltype(List<T>().cbegin()) List<T>::cbegin() const
{
    return const_iterator(head_, this);
}

Конечно, в вашем опубликованном коде есть куча функций, которые вы определили, но не объявили, как, например, operator =, конструкторы, деструктор, который выдал мне ошибки Он также функционирует совершенно корректно, если реализован встроенным. Это воняет мне как ошибка компилятора.

1 голос
/ 17 декабря 2010

Я могу скомпилировать ваш код в VS2008, если я внесу следующие изменения:
template <typename T>
typename const List<T>::const_iterator List<T>::cbegin() const
Я не знаю, почему это дополнительное значение должно иметь значение, но я держу пари, что кто-то делает.

1 голос
/ 17 декабря 2010

This:

// Compiler error
template <typename T>
typename List<T>::const_iterator List<T>::cbegin() const
{
    return const_iterator(head_, this);
}

компилируется с g ++.Но это не так:

// Compiles ok
template <typename T>
typename const List<T>::Iter_ List<T>::cend() const
{
    return const_iterator(tail_, this);
}

Можете ли вы проверить, правильно ли вы пометили свой код.

1 голос
/ 17 декабря 2010

Код:

class Iter_
{
};

template <typename T>
class List {
public:
    // Think of a class Iter_ with ListElem *pCurrentPos and List *pList
    typedef const Iter_ const_iterator;

    const_iterator cbegin() const;
};

template <typename T>
typename List<T>::const_iterator List<T>::cbegin() const {}

int main()
{
    List<int> foo;

    List<int>::const_iterator iter = foo.cbegin();

    return 0;
}

прекрасно компилируется с gcc 4.2.2 (который, я признаю, устарел).

Однако, похоже, в вашем файле есть дублирующее определение (s) что вы удалили, когда вместо этого изменили тип на Iter_.Не могли бы вы дать нам полный пример кода, который не компилируется с сообщением об ошибке?

РЕДАКТИРОВАТЬ: Я попытался еще раз с вашим большим примером.В нем было множество ошибок (пропущенных функций и пропусков size_), которые я исправил.

После этого cbegin прекрасно скомпилировался, а cend нет, потому что вы написали typename const List<T>::Iter_ List<T>::cend() const вместо const typename List<T>::Iter_ List<T>::cend() const (const НЕ является частью того, что квалифицировано как typename).

Если cbegin действительно является причиной ошибки, для меня это звучит как ошибка компилятора.

...