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
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 开发者_如何学Goexamples; 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;
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.
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.
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!
精彩评论