我的代码中有以下方案,
#include <memory>
#include <iostream>
template <class T>
struct A {};
template <class T>
struct Z {};
template <class T, class U>
struct B : A<T>, Z<U> {};
template <class T>
struct C : B<T, T> {};
template <class T>
void foo(const std::shared_ptr<A<T>>& a)
{
std::cout << "HI !" << std::endl;
}
int main()
{
auto c = std::make_shared<C<char>>();
foo(c);
return 0;
}
但编译器无法正确替换模板参数:
main.cpp: In function 'int main()':
main.cpp:26:10: error: no matching function for call to 'foo(std::shared_ptr<C<char> >&)'
foo(c);
^
main.cpp:17:6: note: candidate: template<class T> void foo(const std::shared_ptr<A<T> >&)
void foo(const std::shared_ptr<A<T>>& a)
^~~
main.cpp:17:6: note: template argument deduction/substitution failed:
main.cpp:26:10: note: mismatched types 'A<T>' and 'C<char>'
foo(c);
^
所以我将foo
更改为:
template <class T, template <class> class U>
void foo(const std::shared_ptr<U<T>>& a)
{
std::cout << "HI !" << std::endl;
}
它有效,但它感觉不对,因为对类型的模板约束现在真的很松散,这通常会导致更加混淆的错误消息。
有没有更好的方法来处理这类模板扣除失败?
答案 0 :(得分:1)
有一种方法。您可以选择使用一些模板元编程来手动约束模板,以便只使用指针类型(可以扩展为使用各种指针类型,但只适用于unique_ptr
和shared_ptr
可以使用case(实际上包含具有element_type
类型别名的所有智能指针类型)的派生类。
#include <memory>
#include <iostream>
namespace {
/**
* Get the template type of a template template type
*/
template <typename T>
struct GetType;
template <typename T, template <typename...> class TT>
struct GetType<TT<T>> {
using type = T;
};
} // namespace <anonymous>
template <class T>
struct A {};
template <class T>
struct Z {};
template <class T, class U>
struct B : A<T>, Z<U> {};
template <class T>
struct C : B<T, T> {};
template <class T, typename std::enable_if_t<std::is_base_of<
A<typename GetType<typename std::decay_t<T>::element_type>::type>,
typename std::decay_t<T>::element_type>::value>* = nullptr>
void foo(const T&)
{
std::cout << "HI !" << std::endl;
}
int main()
{
auto c = std::make_shared<C<char>>();
auto c_uptr = std::make_unique<C<char>>();
foo(c);
foo(c_uptr);
return 0;
}
这现在适用于unique_ptr
和shared_ptr
。您可以通过在智能指针上使用带有element_type
的{{1}}检查包含的指针类型(示例中为decltype
)来选择使其更通用,并制作检测是否具有特征的特征传递的指针是一个原始指针,如果是这样,只使用该类型本身。但你明白了。
答案 1 :(得分:1)
我建议对foo
(模板模板一)进行一点改进,其中类接收模板类型参数列表;仅当C<T0, Ts...>
类是A<T0>
的派生类时,才能激活SFINAE。
template <template <typename...> class C, typename T0, typename ... Ts>
typename std::enable_if<std::is_base_of<A<T0>, C<T0, Ts...>>::value>::type
foo (const std::shared_ptr<C<T0, Ts...>>& a)
{ std::cout << "HI !" << std::endl; }
因此,您可以使用foo()
,A<T>
和B<T, U>
拨打C<T>
,但不能使用Z<T>
或(例如)std::vector<T>
{ p>
以下是完整的可编辑示例
#include <memory>
#include <vector>
#include <iostream>
template <typename T>
struct A {};
template <typename T>
struct Z {};
template <typename T, typename U>
struct B : A<T>, Z<U> {};
template <typename T>
struct C : B<T, T> {};
template <template <typename...> class C, typename T0, typename ... Ts>
typename std::enable_if<std::is_base_of<A<T0>, C<T0, Ts...>>::value>::type
foo (const std::shared_ptr<C<T0, Ts...>>& a)
{ std::cout << "HI !" << std::endl; }
int main ()
{
foo(std::make_shared<A<char>>());
foo(std::make_shared<B<char, long>>());
foo(std::make_shared<C<char>>());
// foo(std::make_shared<Z<char>>()); // compilation error
// foo(std::make_shared<std::vector<char>>()); // compilation error
}
答案 2 :(得分:1)
在您的示例中,您不需要转移所有权,因此您应该更喜欢通过const引用传递参数,然后它按预期工作:
template <class T>
void foo(const A<T>& a)
{
std::cout << "HI !" << std::endl;
}
int main()
{
auto c = std::make_shared<C<char>>();
foo(*c);
}
如果您确实需要通过shared_ptr
,请参阅另一个答案。