2016-10-31 6 views
2

В моем текущем проекте мы строим для Linux и Windows одновременно. К сожалению, поскольку некоторые платформы вызывают проблемы с MSVC, очень старый. Мы используем MSVC 2010. И gcc мы используем относительно новый и умный, который имеет версию 4.8.Кастинг std :: tr1 :: shared_ptr <T> и std :: shared_ptr <T> с такой же функцией, но с разными перегрузками

ниже код компиляции в GCC, но MSCV ворчит по этому поводу:

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 начал пилить после того, как я добавляю вторую перегрузку для станд :: tr1 :: shared_ptr. В компиляции ошибки я получаю несколько раз:

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

Есть ли у вас, ребята, есть решение для моего случая?

+0

'# ifdef' это на платформах, это не работает? – Yakk

+0

Возможно, что один из них является «использованием shared_ptr = другой». В этом случае он является одним и тем же типом и не может использоваться для перегрузки. –

+0

@Yakk Мне жаль, что я не смог бы легко его протестировать, но как только я сделаю смену, машина автоматической сборки будет запущена, а процесс сборки займет 1-2 часа. К сожалению, я должен быть уверен, прежде чем применить. –

ответ

2

Сделайте свой шаблон Cast функций принимает параметр шаблона шаблона:

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


Оставляя оригинальный ответ для потомков. Он плохо сформирован на VC++ (хотя он работает так, как ожидалось), потому что нет действительной специализации функции.

Отключить вторую перегрузку, если std::shared_ptr и std::tr1::shared_ptr - это то же самое (они находятся на VC++ 10, они не для моего 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); 
} 

Следующая компилирует на обоих VC++ 10 и в latest gcc. К сожалению, это плохо формируется на VC++ 10 (несмотря на работу, как и ожидалось)

#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

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

+0

Этот подход намного лучше, чем подход #ifdef с моей точки зрения. Тестирование сейчас –

+0

Единственный недостаток заключается в том, что вы в конечном итоге с плохо сформированной программой, которая происходит для компиляции. Этот метод не является законным C++, но стандартное состояние не требует диагностики в этом случае (поскольку определение того, что специализация функции шаблона не может быть действительной, является Halt-hard). – Yakk

+0

@Yakk почему он плохо сформирован? – krzaq

2

Это Cast, который может принимать либоstd::tr1::shared_ptr или std::shared_ptr в одну template функцию. Это следует DRY (не повторяй себя) и избегает тонких ловушек с альтернативными решениями:

template<class T, template<class>class Sp, class U> 
Sp<T> Cast(const Sp<U>& spObject) 
{ 
    typedef Sp<T> R; 
    // manual implementation of the dynamic shared cast 
    // as we don't know if we want to use tr1 or not: 
    T* out = dynamic_cast<T*>(spObject.get()); 
    if (!out) 
    return R(); 
    // alising ctor, shares refcount block with spObject 
    // but uses out pointer: 
    return R(spObject, out); 
} 

Это правовой C++ и должен работать как для std::tr1::shared_ptr и std::shared_ptr.

Любая функция шаблона, которую вы пишете, должна иметь набор аргументов шаблона, который, если вы передадите их, шаблон действителен. Невыполнение этого условия делает вашу программу плохо сформированной, не требуется диагностика.

live example.

Опасения, которые у меня есть, это то, что MSVC2010 может не правильно выводить аргументы шаблона шаблона (я имею в виду, это C++ 03, но это MSVC2010), или что std::tr1::shared_ptr может отсутствовать alizing ctor.

Если вам необходимо ограничить Cast работать только с std::shared_ptr или std::tr1::shared_ptr, можно добавить тест SFINAE, что Sp<U> является один из этих двух.

std::is_same< std::shared_ptr<U>, Sp<U> >{} 
|| std::is_same< std::tr1::shared_ptr<U>, Sp<U> >{} 

, но я сомневаюсь, что это необходимо. Тест становится избыточным в системе, где они одного типа, но избыточные тесты являются законными, избыточные перегрузки - нет.

+1

После изменения псевдонима типа от использования до typedef он компилируется на VC10. – krzaq