我想完善除特定类型之外的前向可变参数
I want to perfect forward variadic arguments except for a specific type
我有以下
#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_if
和 std::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/
真正的问题是,我该如何更改这个函数,以便在我向工厂函数传递一个已经存在的指针时调用非模板函数?
有通用的方法吗?
调用时
handle<int> h2(h);
你打电话
handle(handle<_type> const & other) : mData(make_pptr(*(other.mData)))
{}
并且 *other.mData
是一个 std::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))))
{}
解决您的问题。
或添加重载以供简单参考。
正如 Jarod 的 所解释的,在构造函数中
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
}
我有以下
#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_if
和 std::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/
真正的问题是,我该如何更改这个函数,以便在我向工厂函数传递一个已经存在的指针时调用非模板函数?
有通用的方法吗?
调用时
handle<int> h2(h);
你打电话
handle(handle<_type> const & other) : mData(make_pptr(*(other.mData)))
{}
并且 *other.mData
是一个 std::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))))
{}
解决您的问题。
或添加重载以供简单参考。
正如 Jarod 的
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
}