我希望完善除了特定类型之外的可变参数

时间:2015-05-26 16:42:51

标签: c++ shared-ptr smart-pointers c++14 perfect-forwarding

我有以下

#include <iostream>
#include <memory>

template<typename _type>
class handle
{
  using ptr = std::shared_ptr<_type>;
  using pptr = std::shared_ptr<ptr>;
public:
  handle(handle<_type> const & other) :
    mData(make_pptr(*(other.mData)))
  {}


  handle(_type && data) :
    mData(make_pptr(std::move(data)))
  {}
private:
  pptr mData;

  template<typename ..._args>
  constexpr auto make_ptr(_args && ...args)
  {
    return std::make_shared<_type>(std::forward<_args>(args)...);
  }

  constexpr auto make_pptr(ptr const & pointer)
  {
    return std::make_shared<ptr>(pointer);
  }

  template<typename ..._args>
  constexpr auto make_pptr(_args && ...args)
  {
    return std::make_shared<ptr>(make_ptr(std::forward<_args>(args)...));
  }
};

int main()
{
  handle<int> h = 5;
  handle<int> h2(h);
}

使用g++-4.9 --std=c++14 -O0 -o main main.cpp代码编译

handle<int> h2(h);

无法编译。问题函数是

的所有重载
make_pptr

据我所知,模板函数将始终被选中,因为编译器试图找到专用函数调用,而完美转发正是这样做的。

我发现以下两个页面似乎处理了特征类型std::enable_ifstd::is_same的问题。

https://akrzemi1.wordpress.com/2013/10/10/too-perfect-forwarding/

http://www.codesynthesis.com/~boris/blog/2012/05/30/perfect-forwarding-and-overload-resolution/

实际问题是,如何更改此函数,以便在我将工厂函数传递给现有指针时调用非模板函数?

有没有通用的方法呢?

2 个答案:

答案 0 :(得分:2)

正如Jarod的answer在构造函数中所解释的那样

handle(handle<_type> const & other) :
  mData(make_pptr(*(other.mData)))
{}

您使用make_pptr类型的参数调用shared_ptr<_type>&,这使得make_pptr的完美转发重载比采用shared_ptr<_type> const&的更好匹配。您可以在他显示的时候将参数转换为const&,或者您可以添加另一个make_pptr的重载,该重载采用非const左值参考。

constexpr auto make_pptr(ptr & pointer)
{
  return std::make_shared<ptr>(pointer);
}

另一种选择是限制完美的转发过载,以便仅当参数包的第一个参数不是shared_ptr<_type>时才可行。

一些帮助器,用于评估参数包中的第一个类型是否为shared_ptr<T>

namespace detail
{
    template<typename... _args>
    using zeroth_type = typename std::tuple_element<0, std::tuple<_args...>>::type;

    template<typename T, bool eval_args, typename... _args>
    struct is_shared_ptr
    : std::false_type
    {};

    template<typename T, typename... _args>
    struct is_shared_ptr<T, true, _args...>
    : std::is_same<std::decay_t<zeroth_type<_args...>>,
                   std::shared_ptr<T>
                  >
    {};
}

然后如下限制完美转发make_pptr

template<typename ..._args,
         typename = std::enable_if_t<
                        not detail::is_shared_ptr<_type, sizeof...(_args), _args...>::value
                    >
        >
constexpr auto make_pptr(_args && ...args)
{
  return std::make_shared<ptr>(make_ptr(std::forward<_args>(args)...));
}

我还必须更改make_ptr重载,因为您在示例中定义的方式要求_type可以nullptr构建。

constexpr auto make_ptr()
{
  return std::make_shared<_type>();
  // no nullptr arg above, shared_ptr default ctor will initialize _type* to nullptr
}

Live demo

答案 1 :(得分:0)

致电

handle<int> h2(h);

你打电话

handle(handle<_type> const & other) : mData(make_pptr(*(other.mData)))
{}

*other.mDatastd::shared_ptr<_type>&,因此您可以致电

template<typename ..._args>
constexpr auto make_pptr(_args && ...args)

这是完全匹配。

您可以使用

强制const
handle(handle<_type> const & other) :
    mData(make_pptr(static_cast<const ptr&>(*(other.mData))))
{}

解决您的问题。

或添加重载以供简单参考。

相关问题