Strange VC++ compile error, C2244

2019-01-20 15:14发布

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条回答
三岁会撩人
2楼-- · 2019-01-20 15:55

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.

查看更多
不美不萌又怎样
3楼-- · 2019-01-20 16:05

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>();
}
查看更多
Fickle 薄情
4楼-- · 2019-01-20 16:14

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...

查看更多
登录 后发表回答