使用模板模板参数和 enable_if 正确选择重载
Getting overload selection right with template template params and enable_if
我在 VS2013 中有一个模板函数,旨在对提供给它的任何对象执行 "deep copy"。一种重载是针对普通类型的,只需调用 operator=。但是我也有一个重载设计用于将 shared_ptr 的向量用于我自己的 Shape-class 对象,它只能通过调用 clone() 成员函数来复制。
struct Shape { virtual std::shared_ptr<Shape> clone() const = 0; };
struct Rectangle : public Shape { virtual std::shared_ptr<Shape> clone() const override; };
所以我有这个重载,编译器选择它就好了
template<class SHP>
inline std::vector<std::shared_ptr<SHP>> deep_copy(
const std::vector<std::shared_ptr<SHP>>& val,
typename std::enable_if<std::is_base_of<Shape, SHP>::value>::type** = nullptr)
{
// ... blah blah blah
}
std::vector<std::shared_ptr<Rectangle>> objects;
auto objects2 = deep_copy(objects);
然后我想更改它以获取 shared_ptr 的任何非键控集合(例如列表)。好的,没问题,我实际上做到了...
template<class COLL>
inline COLL deep_copy(const COLL& val,
typename std::enable_if<std::is_base_of<Shape, typename COLL::value_type::element_type>::value>::type** = nullptr)
但此语法确实 无法确保集合包含shared_ptr。它只是确保它的 value_type 有一个嵌套的 element_type 是某种形状
所以我的问题是,确保集合内容实际上是 std::shared_ptr 派生自 Shape 的语法是什么?
我已经使用模板模板参数对此进行了多次尝试,但我总是搞砸了。
让我们从测试 T
是否为 shared_ptr
的概念开始:
template<typename T>
struct is_shared_ptr : std::false_type{};
template<typename T>
struct is_shared_ptr<std::shared_ptr<T>> : std::true_type{};
现在像
std::cout << std::boolalpha << is_shared_ptr<std::shared_ptr<Shape>>::value << std::endl;
会输出
true
接下来,如果某物由于使用该概念而给出 true_type
,我们接下来要检查它的 element type
:
template<typename T>
using element_t = typename std::decay_t<T>::element_type;
查看 returned 类型是否派生自 Shape
。
让我们创建另一个辅助别名来从集合中获取 value_type
:
template<typename T>
using collection_vt = typename std::decay_t<T>::value_type;
现在我们可以将这些概念组合成一个看起来非常粗糙的概念:
template<template<class...> class C, typename U, typename... A>
auto DoTheThing(const C<U, A...>& _collection) -> std::enable_if_t<
is_shared_ptr<collection_vt<decltype(_collection)>>::value &&
std::is_base_of<Shape, element_t<collection_vt<decltype(_collection)>>>::value
>
{
std::cout << _collection.size() << std::endl;
}
初始模板参数用于接受相对通用的容器。然后我们使用尾随 return 类型首先确保 value_type
是 shared_ptr
,然后还要检查 shared_ptr
的 element_type
是否来自 Shape
。因为,一旦成功,来自 enable_if
的类型是 void
,函数的 return 类型变为无效。
Live Demo
这是测试:
std::vector<std::shared_ptr<Shape>> vec1;
DoTheThing(vec1); // success
std::vector<Wrapper<Shape>> vec2;
//DoTheThing(vec2); // error
std::vector<std::shared_ptr<int>> vec3;
//DoTheThing(vec3); // error
std::list<std::shared_ptr<Shape>> vec4;
DoTheThing(vec4); // success
我在 VS2013 中有一个模板函数,旨在对提供给它的任何对象执行 "deep copy"。一种重载是针对普通类型的,只需调用 operator=。但是我也有一个重载设计用于将 shared_ptr 的向量用于我自己的 Shape-class 对象,它只能通过调用 clone() 成员函数来复制。
struct Shape { virtual std::shared_ptr<Shape> clone() const = 0; };
struct Rectangle : public Shape { virtual std::shared_ptr<Shape> clone() const override; };
所以我有这个重载,编译器选择它就好了
template<class SHP>
inline std::vector<std::shared_ptr<SHP>> deep_copy(
const std::vector<std::shared_ptr<SHP>>& val,
typename std::enable_if<std::is_base_of<Shape, SHP>::value>::type** = nullptr)
{
// ... blah blah blah
}
std::vector<std::shared_ptr<Rectangle>> objects;
auto objects2 = deep_copy(objects);
然后我想更改它以获取 shared_ptr 的任何非键控集合(例如列表)。好的,没问题,我实际上做到了...
template<class COLL>
inline COLL deep_copy(const COLL& val,
typename std::enable_if<std::is_base_of<Shape, typename COLL::value_type::element_type>::value>::type** = nullptr)
但此语法确实 无法确保集合包含shared_ptr。它只是确保它的 value_type 有一个嵌套的 element_type 是某种形状
所以我的问题是,确保集合内容实际上是 std::shared_ptr 派生自 Shape 的语法是什么?
我已经使用模板模板参数对此进行了多次尝试,但我总是搞砸了。
让我们从测试 T
是否为 shared_ptr
的概念开始:
template<typename T>
struct is_shared_ptr : std::false_type{};
template<typename T>
struct is_shared_ptr<std::shared_ptr<T>> : std::true_type{};
现在像
std::cout << std::boolalpha << is_shared_ptr<std::shared_ptr<Shape>>::value << std::endl;
会输出
true
接下来,如果某物由于使用该概念而给出 true_type
,我们接下来要检查它的 element type
:
template<typename T>
using element_t = typename std::decay_t<T>::element_type;
查看 returned 类型是否派生自 Shape
。
让我们创建另一个辅助别名来从集合中获取 value_type
:
template<typename T>
using collection_vt = typename std::decay_t<T>::value_type;
现在我们可以将这些概念组合成一个看起来非常粗糙的概念:
template<template<class...> class C, typename U, typename... A>
auto DoTheThing(const C<U, A...>& _collection) -> std::enable_if_t<
is_shared_ptr<collection_vt<decltype(_collection)>>::value &&
std::is_base_of<Shape, element_t<collection_vt<decltype(_collection)>>>::value
>
{
std::cout << _collection.size() << std::endl;
}
初始模板参数用于接受相对通用的容器。然后我们使用尾随 return 类型首先确保 value_type
是 shared_ptr
,然后还要检查 shared_ptr
的 element_type
是否来自 Shape
。因为,一旦成功,来自 enable_if
的类型是 void
,函数的 return 类型变为无效。
Live Demo
这是测试:
std::vector<std::shared_ptr<Shape>> vec1;
DoTheThing(vec1); // success
std::vector<Wrapper<Shape>> vec2;
//DoTheThing(vec2); // error
std::vector<std::shared_ptr<int>> vec3;
//DoTheThing(vec3); // error
std::list<std::shared_ptr<Shape>> vec4;
DoTheThing(vec4); // success