简体   繁体   中英

Why can't the compiler differentiate between typedef and non-typedef?

Sorry for the long title.

I have a typedef in a class List:

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;
};

and the definition outside of the class, but inside the header file.

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

This produces the error C2373: Redefinition; different type modifiers Redefinition; different type modifiers

I rewrote the function like so:

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

and the error is gone; the program compiles correctly. (think away the fact I'm not returning anything in these examples; it's irrelevant to the example.)

What is the compiler interpreting with the erroneous version that prevents successful compilation that the second version does not, and how can I remedy this?

More Code

I'm using VS2008

The (fuller) code example I'm currently programming:

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);
}

The error I get when instantiating cbegin() is that you are passing (const) this to the constructor which takes a non-const pointer to the List.

Basically I doubt this idea works that well.

 typedef const Iter_   const_iterator;

The code:

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;
}

compiles just fine with gcc 4.2.2 (which I admit is old).

However it sounds like there's an actual duplicate definition in your file(s) that you removed when you changed the type to Iter_ instead. Could you give us a full code example that fails to compile with the error message?

EDIT: I tried again with your bigger example. It had a ton of errors (missing function delcarations and missing size_) that I fixed.

After that, cbegin compiled fine while cend didn't, because you wrote typename const List<T>::Iter_ List<T>::cend() const instead of const typename List<T>::Iter_ List<T>::cend() const (const is NOT part of the thing qualified with typename ).

If cbegin really is the one generating an error it sounds like a compiler bug to me.

This:

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

compiles with g++. But this doesn't:

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

Can you please check that you have labelled your code correctly.

I can compile your code in VS2008 if I make the following change:
template <typename T>
typename const List<T>::const_iterator List<T>::cbegin() const
I don't know why that extra const should make the difference, but I'll bet someone does.

Edit:

How very strange. I even used automatic type deduction to definitely get the correct return type, and it still rejected my code.

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

Of course, your posted code has a bunch of functions that you defined but didn't declare, like operator=, constructors, destructor, which threw errors for me. It also functions perfectly correctly if implemented inline. This stinks like a compiler bug to me.

Testing this on GCC 4.5.0 (MinGW), the following code compiles fine:

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 {}

If I change the last line to

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

it won't compile. Mark gave an excellent explanation for this, typename List<T>::Iter is a single thing that shouldn't be separated by inserting random type modifiers inside. This also works fine:

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

GCC behavior makes perfect sense to me, so I think this is a bug in the MSVC compiler.

Let's try reducing this to the simplest possible. Does MSVC 2008 compile this file?

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;
}

If not, you have a small demonstration of a compiler bug!

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM