使用相同的函数但重载不同的 std::tr1::shared_ptr<T> 和 std::shared_ptr<T>

Posted

技术标签:

【中文标题】使用相同的函数但重载不同的 std::tr1::shared_ptr<T> 和 std::shared_ptr<T>【英文标题】:Casting std::tr1::shared_ptr<T> and std::shared_ptr<T> with same function but different overloads 【发布时间】:2016-10-31 09:33:34 【问题描述】:

在我当前的项目中,我们同时为 Linux 和 Windows 构建。 不幸的是,由于一些平台问题,我们的 MSVC 已经很老了。我们使用的是 MSVC 2010。而 gcc 我们使用的是相对较新且更智能的版本,其版本为 4.8 。

下面的代码在 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);

在我为 std::tr1::shared_ptr 添加第二个重载后,MSVC 开始唠叨。 我反复遇到的编译错误:

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

你们对我的情况有解决方案吗?

【问题讨论】:

#ifdef 它在平台上不起作用? 其中一个可能是using shared_ptr = the other one。在这种情况下,它是同一类型,不能用于重载。 @Yakk 我希望我可以轻松测试它,但是一旦我做出更改,自动构建机器将被触发并且构建过程将需要 1-2 小时。不幸的是,我需要先确定,然后再申请。 @BoPersson 我认为这也是问题所在。但正如我在前面所说的那样。不幸的是,我需要确定至少喜欢 %90% 的评论。 【参考方案1】:

使您的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_ptrstd::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 排除第二个重载,但我不确定应该检查哪些条件。

【讨论】:

从我的角度来看,这种方法比#ifdef 方法要好得多。立即测试 唯一的缺点是你最终会得到一个格式错误的程序,而它恰好可以编译。这种技术不是合法的 C++,但标准规定在这种情况下不需要诊断(因为确定模板函数的任何特化都不有效是 Halt-hard)。 @Yakk 为什么格式不正确? @kadir enable 如果某些参数集通过测试 是有效的 C++。这不是这里的情况。在 msvc 上,没有T 通过测试。 @kadir 在 msvc 上,T 使此答案中的模板合法的可能性为零。这在标准下是明确非法的,尽管编译器是非法的(格式错误的程序),但编译器被明确允许不生成错误消息。没有什么可以理解的。它可以编译和工作,但这并不意味着它是有效的 C++。【参考方案2】:

这是一个Cast,可以在一个template 函数中使用任一 std::tr1::shared_ptrstd::shared_ptr。这遵循 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_ptrstd::shared_ptr

您编写的任何模板函数都必须有一组模板参数,如果您将它们传入,则模板是有效的。不这样做会使您的程序格式错误,不需要诊断。

live example.

我担心的是 MSVC2010 可能无法正确推断模板模板参数(我的意思是,这是 C++03,但这是 MSVC2010),或者 std::tr1::shared_ptr 可能缺少别名 ctor。

如果您需要将 Cast 限制为仅与 std::shared_ptrstd::tr1::shared_ptr 一起使用,您可以添加一个 SFINAE 测试,证明 Sp&lt;U&gt; 是这两者之一。

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

但我怀疑这是必需的。测试在它们是相同类型的系统上变得冗余,但冗余测试是合法的,冗余重载则不是。

【讨论】:

在将类型别名从 using 更改为 typedef 后,它在 VC10 上编译。

以上是关于使用相同的函数但重载不同的 std::tr1::shared_ptr<T> 和 std::shared_ptr<T>的主要内容,如果未能解决你的问题,请参考以下文章

什么是 重载 ?为什么要重载?有何特点?

重载覆盖

使用相同的函数但重载不同的 std::tr1::shared_ptr<T> 和 std::shared_ptr<T>

方法的重载

请简述重载和重写的区别

课程作业03-动手动脑(随机数函数重载50!)