4

考虑以下模板函数:

template <class T>
const T* DoSomething(const T& t)
{
    auto& id = typeid(T);
    cout << "Type is " << id.name() << ", and we have a ";
    cout << "ref of one\n";
    return &t;
}

template <class T>
T* DoSomething(T* t)
{
    auto& id = typeid(T);
    cout << "Type is " << id.name() << ", and we have a ";
    cout << "pointer to one \n";
    return t;
}

template <class T, template <class> class container>
T* DoSomething(const container<T>& t)
{
    auto& type_id = typeid(T);
    auto& container_id = typeid(container<T>);
    cout << "Type is " << type_id.name() << ", and we have a ";
    cout << container_id.name() << "of one\n";
    return t.get();
}

template <class T, template <class,class> class container, template <class> class deleter = default_delete>
T* DoSomething(const container<T, deleter<T>>& t)
{
    auto& type_id = typeid(T);
    auto& container_id = typeid(container<T,deleter<T>>);
    cout << "Type is " << type_id.name() << ", and we have a ";
    cout << container_id.name() << "of one\n";
    return t.get();
}

目标是能够将普通引用、指针或智能指针传递给它们,并使用重载和模板规范来调用正确的函数。以下驱动代码按预期工作:

char r('r');
DoSomething(r);  
DoSomething(&r);
shared_ptr<char> s(new char ('s'));
unique_ptr<char> u(new char ('u'));
DoSomething(s);
DoSomething(u);

但是,考虑一下如果我们尝试这样做会发生什么:

vector<int> v {1,2};
DoSomething(v);

现在,我们得到一个编译错误。编译器决定使用的 DoSomething 版本是第 4 个版本。在这里面,我们引用了一个函数 get(),这个向量没有。如果编译器以某种方式选择 DoSomething 的第一个定义,它会编译得很好,并按我的意图工作。

那么我可以将第 3 和第 4 特化限制为仅在模板模板参数包含 get() 方法时匹配吗?有没有办法让这件事发生,也许使用特征、SFINAE 或其他更高级的模板技术?

4

3 回答 3

4

编译器决定使用的 DoSomething 版本是第 4 个版本。

因为std::vector<T, std::allocator<T>>是模板参数的精确匹配container并且std::allocator<T>是模板参数的精确匹配deleter,并且const container<T, deleter<T>>&const T&第4个重载被选择为函数模板的部分排序规则的最佳匹配。

那么我可以将第 3 和第 4 特化限制为仅在模板模板参数包含 get() 方法时匹配吗?

是的,您可以告诉编译器该函数返回任何t.get()返回值:

template <class T, template <class> class container>
auto DoSomething(const container<T>& t) -> decltype(t.get())
{
    auto& type_id = typeid(T);
    auto& container_id = typeid(container<T>);
    cout << "Type is " << type_id.name() << ", and we have a ";
    cout << container_id.name() << "of one\n";
    return t.get();
}

如果t.get()不是有效表达式,则模板参数推导失败,因为参数T无法成功替换到函数签名中,因此该函数将不是可行的重载,而是使用第一个重载。

于 2013-11-14T11:14:26.907 回答
2

当使用带有自定义删除器的 std::unique_ptr 时,OP 代码存在缺陷:

struct Deleter {
    void operator()(char*) const {}
};

unique_ptr<char, Deleter> u(new char ('u'), Deleter());
DoSomething(u) // The first is applied.

通过 SFINAE 使用鸭子打字很麻烦。

template <typename T, typename U>
struct X { 
    // Is this a get like a get of a smart pointer !?  
    T* get();
} 

我推荐user1095108的解决方案。

于 2013-11-14T12:11:15.530 回答
1

我会试试这个:

template <typename>
struct is_smart : ::std::false_type
{
};

template <typename T>
struct is_smart<::std::shared_ptr<T> > : ::std::true_type
{
};

像这样使用:

::std::cout << is_smart<container<T> >{} << ::std::endl;

但我宁愿这样覆盖:

template <class T>
T* DoSomething(const ::std::shared_ptr<T>& t)
{
}

对于您打算支持的每个容器和智能指针类型。

于 2013-11-14T11:14:15.047 回答