Не удается преобразовать из типа x в тип x?

При компиляции (Microsoft Visual С++ 2005 Express) этот фрагмент кода:

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

Я получаю ошибку C2440:

'=': не может преобразовать из 'A (__cdecl *) (void)' в 'A (__cdecl *) (void)'

Это не имеет смысла для меня. Очевидно, что два типа, названные в тексте ошибки, одинаковы. Кроме того, при изменении Foo возвращаемого значения на int такой ошибки нет.

Это ошибка или я что-то не так?

РЕДАКТИРОВАТЬ: Итак, если это ошибка, кто-нибудь знает, как это решить? Может быть, с помощью бросков? Мне нужен этот код для компиляции...

Ответ 1

Это ошибка компилятора. VС++ делает что-то очень странное.

Например, это генерирует очень другое сообщение об ошибке:

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

И это работает:

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

Очевидно, что это путает имя A, которое должно ссылаться на базовый класс.

Добавление typedef не помогло, так же как и декларация в начале struct A, также не соответствует имени как ::A или struct A.

Как ни странно, VС++ 7 прекрасно компилирует его.

Обходной путь: Изменение этого типа:

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

инвертирует результат, теперь VС++ 8 компилируется нормально, а VС++ 7 генерирует то же сообщение об ошибке.

Я думаю, что после завершения неполного типа и того же типа возникает проблема с идентификатором типа.

Все тесты выполняются с помощью Dinkumware Multi-Compiler Test Tool

Ответ 2

Я не уверен, что это ошибка компилятора или нет, но, по крайней мере, она документирована на msdn.
У меня нет компилятора 2005 года, но vs2010 компилирует ваш код, если он записывается следующим образом:

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

Ответ 4

Я попытался отследить проблему, и теперь кажется, что даже не нужно иметь шаблонные функции или вложенные структуры для создания этой странной ошибки.

struct A
{
    typedef A ( * SimpleFuncPtr )( void );
    static void Foo( void )
    {
        SimpleFuncPtr func1 = 0;        // Ok.
        SimpleFuncPtr func2 = func1;    // Ok.
        A ( * func3 )( void ) = func1;  // C2440 on both VS2005 and VS2010
    }
};

Посмотрев на вышеприведенный фрагмент кода, становится очевидным, что это действительно ошибка компилятора (на мой взгляд).