考虑以下模板功能:
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的第一个定义,它将编译正常,并按我的意图工作。
那么,当模板模板参数包含get()方法时,我是否可以限制第3和第4个特化仅匹配?有没有办法可以实现这一点,可能使用特征,SFINAE或其他一些更先进的模板技术?
答案 0 :(得分:4)
编译器决定使用的DoSomething版本是第4版。
因为std::vector<T, std::allocator<T>>
与模板参数container
完全匹配,std::allocator<T>
与模板参数deleter
完全匹配,const container<T, deleter<T>>&
更多专用于const T&
,因此选择第4次重载作为功能模板的部分排序规则的最佳匹配。
那么,当模板模板参数包含get()方法时,我是否可以限制第3和第4个特化仅匹配?
是的,您可以告诉编译器函数返回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
无法成功替换到函数签名中,因此该函数不会成为可行的重载,并且将使用第一次重载。
答案 1 :(得分:2)
我试试这个:
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)
{
}
为您打算支持的每个容器和智能指针类型。
答案 2 :(得分: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的解决方案。