【问题标题】:Function overloading with shared pointer argument ambiguity具有共享指针参数歧义的函数重载
【发布时间】:2013-03-19 11:23:30
【问题描述】:

我想创建一个重载函数,这些函数接受一个指向基类和派生类的共享指针。它似乎适用于引用和原始指针,但不适用于额外派生类的共享指针。见示例代码:

#include <memory>

class Base{};
class Derived : public Base {};
class ExtraDerived : public Derived {};


bool IsBase(Base*){ return true; }
bool IsBase(Derived*){ return false; }

bool IsBase(std::shared_ptr<Base>){ return true; }
bool IsBase(std::shared_ptr<Derived>){ return false; }

int main() 
{
    auto derived = std::make_shared<Derived>();
    auto extra_derived = std::make_shared<ExtraDerived>();
    // works
    auto raw_result_derived = IsBase(derived.get());
    auto raw_result_extra_derived = IsBase(extra_derived.get());
    auto shared_result_derived = IsBase(derived);
    // doesn't work
    auto shared_result_extra_derived = IsBase(extra_derived);
}

在使用 Visual Studio 2012 时,我得到:“错误 C2668:'IsBase':对重载函数的模糊调用”,但当我在此处尝试代码时也得到相同的结果 http://ideone.com/6uoa0p

这似乎不是理想的行为(因为它适用于“原始”内容)。这是模板的限制吗,还有其他原因导致这不起作用还是错误? 我怎样才能让它以最不难看的方式工作?

我能想到的最好的是

//ugly workaround
bool IsBase(std::shared_ptr<Base>, Base*){ return true; }
bool IsBase(std::shared_ptr<Derived>, Derived*){ return false; }
template<typename T> bool IsBase(std::shared_ptr<T> input )
{
    return IsBase(input, input.get());
}

【问题讨论】:

  • 你为什么要那个?只需提供基函数,它就会愉快地接受派生类指针。如果你想捕获派生类对象,你的方法无论如何都不会工作,因为它需要对函数参数的动态类型进行调度才能正确完成。如果程序员在传递之前将Ptr&lt;Derived&gt; 隐式转换为Ptr&lt;Base&gt;,那么这种重载方法就会失效。
  • @JohannesSchaub-litb 我没有想到那种情况。我可能不得不重新考虑我想要什么。

标签: c++ inheritance c++11 shared-ptr overloading


【解决方案1】:

这是模板的限制吗,还有其他原因导致这不起作用还是错误?

不,这不是错误。实际上,您似乎遇到了智能指针的唯一陷阱:std::shared_ptr&lt;base&gt; 可以从 std::shared_ptr&lt;derived&gt;std::shared_ptr&lt;extra_derived&gt; 构造,但是这两个转换序列中没有一个比另一个更好(作为两个用户-定义的相同长度的转换序列)。

但是,您仍然可以通过使用一些 SFINAE 约束来修复您的重载:

#include <type_traits>

// Selected for `std::shared_ptr<Base>`
template<typename T, typename std::enable_if<
    std::is_same<T, Base>::value>::type* = nullptr>
bool IsBase(std::shared_ptr<T>){ return true; }

// Selected for `std::shared_ptr<T>` where T is a class derived from Base,
// but not Base itself
template<typename T, typename std::enable_if<
    std::is_base_of<Base, T>::value &&
    !std::is_same<T, Base>::value
    >::type* = nullptr>
bool IsBase(std::shared_ptr<T>){ return false; }

【讨论】:

  • 遗憾的是,这在 VS2012 上不起作用,但它应该在未来。它适用于这种简单的情况,但如果我对不同的派生类有更多的重载,它可能会成为一个问题。我将不得不考虑我是否真的想要拥有这样的功能,或者不考虑 JohannesSchaub-litb 对我的问题的评论。
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 2013-07-26
  • 1970-01-01
  • 2016-11-16
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多