views:

353

answers:

3

Take a look at this peice of code:

template <typename K,typename T>
Pointer<typename Collection<T>::Iterator> BinaryTree<K,T>::GetBeginning() const
{
    return new BinaryTreeIterator(this,BinaryTreeIterator::Position::atBeginning);
}

When I try to compile it using VSTS 2008, I get:

error C2244: 'BinaryTree<K,T>::GetBeginning' : unable to match function definition to an existing declaration
see declaration of 'BinaryTree<K,T>::GetBeginning'
2>        definition
2>        'Pointer<Collection<T>::Iterator> BinaryTree<K,T>::GetBeginning(void) const'
2>        existing declarations
2>        'Pointer<Collection<T>::Iterator> BinaryTree<K,T>::GetBeginning(void) const'

The declaration:

Pointer<Iterator> GetBeginning() const;

is inside the class. BinaryTree indirectly inherits from Collection, and BinaryTreeIterator indirectly inherits from Iterator, both nested classes of their respective containers.

You can easily see that even in the error report, both definition and declaration are identical. Is there really something wrong here?

I found that microsoft released a hotfix: "Certain template code does not compile, and error C2244 occurs after you install Visual Studio 2005 Service Pack 1". However I couldn't find any reference to VSTS 2008.

So first I wanted to check if anybody could spot a real error in the code at a glance, and if it's VS's fault, does anyone know if the above hotfix is the solution and is relevant for 2008 as well.

+3  A: 

For those interested, I tried writing a minimal sample reproducing the problem:

template <typename T>
struct Pointer {};

template <typename T>
struct Collection {
    struct Iterator {};
};

template <typename K,typename T>
struct BinaryTree : Collection<T>{
    Pointer<typename Collection<T>::Iterator> GetBeginning() const;

    struct BinaryTreeIterator : Collection<T>::Iterator {
     template <typename X>
     BinaryTreeIterator(BinaryTreeIterator*, X) {}
     struct Position {
      static int atBeginning() { return 0; }
     };
    };
};

template <typename K,typename T>
Pointer<typename Collection<T>::Iterator> BinaryTree<K,T>::GetBeginning() const
{
    return Pointer<typename Collection<T>::Iterator>();
}

int main(){
    BinaryTree<int, float> bt;
    bt.GetBeginning();
}

And yes, I get the error too. I can't see any obvious errors in what we've seen of your code, but then again, just this example has used more nested classes and inheritance than most sane C++ programmers do in a year, so I can't say for sure that your code is or isn't correct.

Moreover, I've had to guess quite a bit to piece this together. (What's atBeginning supposed to be? What are the actual class interfaces?)

But I suspect it'd work better (and be more readable and easier to debug) if you didn't inherit everything from everything else.

Update I tried compiling the above with GCC and Comeau's online compiler, and both accepted it. So it seems like it could be a compiler bug.

jalf
Two instances of inheritance is more than most sane C++ programmers do in a year? unlikely.
shoosh
Thanks for supplying the reproduction. Hope it would help someone get a clue. And as for the inheritance scheme, this code is delibaratly not using the stl "flat" containers style. Other than that, I don't see how "everything inherits from everything". The inheritance tree seems preety natural and intuitive to me.
sold
I don't think my example is a accurate, I've had to guess too much at the implementation of the classes. You should really supply your own actual code. And deliberate or not, your code would be much more readable if you'd used the STL style. If I jumpe in front of a train, it'd be pretty damn stupid idea whether or not I did it deliberately.
jalf
Position is an enumuration inside BinaryTreeIterator. atBeginning is one of it's constants.
sold
+1 for finding this testcase. It's pretty difficult to guess what's wrong with the code, i think, but i think this is what happens: VC++ does look into the dependent base-class `Collection<T>`, even though it shouldn. While doing so, it finds the injected-class-name `Collection<T>`. However, when `Collection<T>` is used in the return type out-side the class, it will associate `Collection<T>` using the global template declaration. So it can't match them up.
Johannes Schaub - litb
You can construct the same inequivalence with comeau/gcc by writing this in the class instead: `Pointer<typename BinaryTree<K,T>::Collection::Iterator>` or alternatively `Pointer<typename BinaryTree<K,T>::template Collection<T>::Iterator>`. GCC gives: `Line 23: error: prototype for 'Pointer<typename Collection<T>::Iterator> BinaryTree<K, T>::GetBeginning() const' does not match any in class 'BinaryTree<K, T>'`. Comeau gives something similar.
Johannes Schaub - litb
+1  A: 

The obvious solution which you probably considered is to just define the function inside the class definition instead of redefining it later.

Also, Putting the iterator type in a typedef like so:

template <typename T>
struct Pointer {};

template <typename T>
struct Collection {
    struct Iterator {};
};

template <typename K,typename T>
struct BinaryTree : Collection<T>{
    typedef typename Collection<T>::Iterator Iter;
    Pointer<Iter> GetBeginning() const;

    struct BinaryTreeIterator : Collection<T>::Iterator {
    };
};

template <typename K,typename T>
Pointer<typename BinaryTree<K,T>::Iter> BinaryTree<K,T>::GetBeginning() const
{
    return new BinaryTreeIterator(this,BinaryTreeIterator::Position::atBeginning);
}

int main(){
    BinaryTree<int, float> bt;
    bt.GetBeginning();
}

seems to fix it. Not sure why, possibly a bug...

shoosh
Thanks. To say the truth, I haven't tried that. I am used to seperate the declaration from the implementation, even for templates. And yes, it does solve the problem. I would like to be able to seperate dec's and def's, since this error repeats itself for the rest of the class too, and still don't see why it didn't work. I guess I'll have to do it this way if nothing else comes along. Thanks :)
sold
A: 

It will compile if you change it to this:

template <typename K,typename T>
struct BinaryTree : Collection<T> {
    Pointer<typename BinaryTree<K,T>::Iterator> GetBeginning() const;

};

template <typename K,typename T>
Pointer<typename BinaryTree<K,T>::Iterator> BinaryTree<K,T>::GetBeginning() const
{
    return Pointer<BinaryTree<K,T>::Iterator>();
}

In general, the original code isn't quite right because it implies that GetBeginning() can return any collection, while (I'm assuming) it can only return binary tree collections.

EDIT:

After some digging, it seems that VC++ doesn't handle well injected class names. That is, the original code will compile if you remove from Collection::Iterator in the method declaration:

template <typename K, typename T>
struct BinaryTree : Collection<T> {
    Pointer<typename Collection::Iterator> GetBeginning() const;

};

template <typename K, typename T>
Pointer<typename Collection<T>::Iterator> BinaryTree<K,T>::GetBeginning() const
{
    return Pointer<Collection<T>::Iterator>();
}
Andre