模板的模板推导失败(中间有继承),有没有更好的方法?
Template deduction failure of template of template (with inheritance in-between), is there a better way ?
问题
我的代码中有以下方案,
#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;
}
更好的方法?
它有效,但感觉不对,因为类型上的模板约束现在非常松散,通常最终会产生更混乱的错误消息。
是否有更好的方法来处理此类模板推导失败?
使用 Coliru 上的示例
有办法。您可以选择使用一些模板元编程手动约束模板,以便仅指针类型(可以扩展以使用各种指针类型,但仅适用于 unique_ptr
和 shared_ptr
可以使用派生 类 的情况(实际上所有具有 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
。您可以选择通过在智能指针上使用 decltype
和 .get()
检查包含的指针类型(示例中的 element_type
)来使其更通用,并创建一个特征来检测是否传递的指针是原始指针,如果是这样,则只使用类型本身。但你明白了。
我建议对您的 foo
(模板模板一)进行一些改进,其中 class 接收模板类型参数列表;只有当 C<T0, Ts...>
class 是 A<T0>
.
的派生 class 时,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; }
因此您可以使用 A<T>
、B<T, U>
和 C<T>
调用 foo()
,但不能使用 Z<T>
或(例如)std::vector<T>
以下是完整的可编译示例
#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
}
在你的例子中,你不需要转移所有权,所以你应该更喜欢通过 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
,请参阅其他答案。
问题
我的代码中有以下方案,
#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;
}
更好的方法?
它有效,但感觉不对,因为类型上的模板约束现在非常松散,通常最终会产生更混乱的错误消息。
是否有更好的方法来处理此类模板推导失败?
使用 Coliru 上的示例
有办法。您可以选择使用一些模板元编程手动约束模板,以便仅指针类型(可以扩展以使用各种指针类型,但仅适用于 unique_ptr
和 shared_ptr
可以使用派生 类 的情况(实际上所有具有 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
。您可以选择通过在智能指针上使用 decltype
和 .get()
检查包含的指针类型(示例中的 element_type
)来使其更通用,并创建一个特征来检测是否传递的指针是原始指针,如果是这样,则只使用类型本身。但你明白了。
我建议对您的 foo
(模板模板一)进行一些改进,其中 class 接收模板类型参数列表;只有当 C<T0, Ts...>
class 是 A<T0>
.
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; }
因此您可以使用 A<T>
、B<T, U>
和 C<T>
调用 foo()
,但不能使用 Z<T>
或(例如)std::vector<T>
以下是完整的可编译示例
#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
}
在你的例子中,你不需要转移所有权,所以你应该更喜欢通过 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
,请参阅其他答案。