Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Cannot convert from type x to type x?

When compiling (Microsoft Visual C++ 2005 Express) this piece of code:

struct A
{
    template< typename T > static A Foo( void ) { return A(); }
    struct S
    {
        template< typename T > static S GetInstance( void )
        {
            S Result;
            Result.m_funcFoo = &A::Foo< T >;
            return Result;
        }
        A ( *m_funcFoo )( void );
    };
};

int main(int argc, char* argv[])
{
    A::S::GetInstance< int >();
}

I get a C2440 error:

'=': cannot convert from 'A (__cdecl *)(void)' to 'A (__cdecl *)(void)'

That doesn't make sense to me. The two types named in the error text are obviously the same. Also, when changing Foo's return value to int, there is no such error.

Is it a bug or am I doing something wrong?

EDIT : So, if it's a bug, does anyone know how to solve this? Maybe by using casts? I need this code to compile...

like image 604
Baltram Avatar asked Dec 23 '11 15:12

Baltram


3 Answers

It's a compiler bug. VC++ is doing something very weird.

For example, this generates a very different error message:

struct A
{
    template< typename T > static struct A Foo( void ) { return A(); }
    struct S
    {
        template< typename T > static S GetInstance( void )
        {
            S Result;
            Result.m_funcFoo = &A::Foo< T >;
            return Result;
        }
        A ( *m_funcFoo )( void );
    };
};

sourceFile.cpp(5) : error C3856: 'A::Foo': class is not a class template

And this works:

struct X {};

struct A
{
    template< typename T > static X Foo( void ) { return X(); }
    struct S
    {
        template< typename T > static S GetInstance( void )
        {
            S Result;
            Result.m_funcFoo = &A::Foo< T >;
            return Result;
        }
        X ( *m_funcFoo )( void );
    };
};

Clearly it's getting confused by the name A, which should refer to the base class.

Adding a typedef didn't help, neither does a forward declaration of struct A, neither does qualifying the name as ::A or struct A.

Oddly enough, VC++7 compiles it fine.

Workaround: Changing it like this:

struct A
{
    template< typename T > static A Foo( void ) { return A(); }

    struct S;
};

struct A::S
{
    template< typename T > static S GetInstance( void )
    {
        S Result;
        Result.m_funcFoo = &A::Foo< T >;
        return Result;
    }
    A ( *m_funcFoo )( void );
};

inverts the result, now VC++8 compiles ok and VC++7 generates the same error message.

I think there's a type identity issue between an incomplete type and the same type after completion.

All tests run using the Dinkumware Multi-Compiler Test Tool

like image 164
Ben Voigt Avatar answered Oct 20 '22 09:10

Ben Voigt


I am not sure if it is a compiler bug or not, but at least it is documented on msdn.
I don't have a 2005 compiler at hand but vs2010 compiles your code if write it like this:

struct A
{
    template< typename T > static A Foo( void ) { return A(); }
    struct S
    {
        A ( *m_funcFoo )( void );       

        template< typename T > static S GetInstance( void );
    };
};

template< typename T > 
A::S A::S::GetInstance( void )
{
    S Result;
    Result.m_funcFoo = &A::Foo< T >;
    return Result;
}
like image 26
mkaes Avatar answered Oct 20 '22 09:10

mkaes


Looks like a bug -- it compiles fine on Comeau (http://www.comeaucomputing.com/tryitout).

like image 34
Stuart Golodetz Avatar answered Oct 20 '22 07:10

Stuart Golodetz