Search code examples
c++c++11visual-c++shared-ptr

Casting std::tr1::shared_ptr<T> and std::shared_ptr<T> with same function but different overloads


In my current project we are building for Linux and Windows at the same time. Unfortunately because some platform issues our MSVC is very old. We are using MSVC 2010. And gcc we are using relatively new and smarter which has the version 4.8 .

The code below compile in gcc but MSCV nags about it :

template<class T, class U>
std::shared_ptr<T> Cast( const std::shared_ptr<U>& spObject )   // rename from CastTerrainObject
{
    return std::dynamic_pointer_cast<T>(spObject);
}

template<class T, class U>
std::tr1::shared_ptr<T> Cast( const std::tr1::shared_ptr<U>& spObject ) // rename from CastTerrainObject
{
    return std::tr1::dynamic_pointer_cast<T>(spObject);
}

MSVC began nagging after I add the second overload for std::tr1::shared_ptr. The compile errors I am getting repeatedly :

error C2995: 'std::tr1::shared_ptr<_Ty> Cast(const std::tr1::shared_ptr<_Ty2> &)' : function template has already been defined

And 

 error C2440: 'initializing' : cannot convert from 'std::tr1::shared_ptr<_Ty> (__cdecl *)(const std::tr1::shared_ptr<_Ty2> &)' to 'std::tr1::shared_ptr<_Ty>'

Do you guys have a solution for my case?


Solution

  • Make your Cast function template take a template template parameter:

    template<typename T, template<class> class SP, class U>
    SP<T> Cast2(SP<U> const& sp) {
        using std::dynamic_pointer_cast;
        using std::tr1::dynamic_pointer_cast;
        return dynamic_pointer_cast<T>(sp);
    }
    

    demo


    Leaving the original answer for posterity. It is ill-formed on VC++ (though it works as expected), because there is no valid specialization of the function.

    Disable the second overload if std::shared_ptr and std::tr1::shared_ptr are the same thing (they are on VC++ 10, they are not for my gcc).

    template<class T, class U>
    typename std::enable_if<
        !std::is_same< std::shared_ptr<T>, std::tr1::shared_ptr<T> >::value,
        std::tr1::shared_ptr<T>
    >::type
    Cast( const std::tr1::shared_ptr<U>& spObject ) // rename from CastTerrainObject
    {
        return std::tr1::dynamic_pointer_cast<T>(spObject);
    }
    

    The following compiles on both VC++ 10 and the latest gcc. Unfortunately, it's ill-formed on VC++10 (despite working as expected)

    #include <memory>
    #include <type_traits>
    #ifndef _WIN32
    #include <tr1/type_traits>
    #include <tr1/shared_ptr.h>
    #endif
    
    template<class T, class U> // rename from CastTerrainObject
    std::shared_ptr<T> Cast( const std::shared_ptr<U>& spObject )
    {
        return std::dynamic_pointer_cast<T>(spObject);
    }
    
    template<class T, class U>
    typename std::enable_if<
        !std::is_same< std::shared_ptr<T>, std::tr1::shared_ptr<T> >::value,
        std::tr1::shared_ptr<T>
    >::type
    Cast( const std::tr1::shared_ptr<U>& spObject ) // rename from CastTerrainObject
    {
        return std::tr1::dynamic_pointer_cast<T>(spObject);
    }
    
    struct B{ virtual ~B(){} };
    struct D:B{};
    
    int main()
    {
        Cast<B>(std::make_shared<D>());
    }
    

    demo

    You could also ifdef the second overload away, but I'm not sure which conditions should be checked.