使用模板模板参数和enable_if正确选择重载

Getting overload selection right with template template params and enable_if

本文关键字:选择 if 重载 enable 参数      更新时间:2023-10-16

我在VS2013中有一个模板函数,旨在执行任何给定对象的"深层复制"。 一个重载适用于普通类型,只需调用 operator=。 但是我也有一个重载,旨在处理shared_ptr到我自己的 Shape 类对象的向量,这只能通过调用 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_type,我们接下来要检查其element type

template<typename T>
using element_t = typename std::decay_t<T>::element_type;

查看返回的类型是否派生自 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;
}

初始模板参数用于接受相对通用的容器。然后我们使用尾随返回类型首先确保value_typeshared_ptr,然后检查shared_ptrelement_type是否派生自Shape。因为成功后,enable_if的类型是void的,函数的返回类型变为无效。

现场演示

这是测试:

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