为什么 C++ 模板类型匹配与 base class 引用不匹配,如何使其与 base class 引用匹配?
why C++ template type matching does not match to base class refrence, how can I make it match to base class refrence?
这是我的C++程序代码
#include <iostream>
#include <memory>
using namespace std;
template <typename T>
struct A { T x; };
template <typename T>
struct B:A<T> { };
template <typename T>
void func(const A<T>& t) {
cout<<"2"<<endl;
}
template <typename T>
void func(const T& t) {
cout<<"1"<<endl;
}
int main() {
B<int> b;
func(b);
}
并打印 1
。但我期望的是函数调用打印 2
。为什么 B<int>b
匹配 const T&
而不是 const A<T>&
。我怎样才能让它匹配 const A<T>&
?
解决方案 1
您可以使用 std::enable_if
和模板化模板参数来更好地匹配函数,例如:
#include <iostream>
#include <memory>
#include <type_traits>
using namespace std;
template <typename T>
struct A { T x; };
template <typename T>
struct B:A<T> { };
template <template <typename...> typename T, typename ...Args, typename = std::enable_if_t<std::is_base_of_v<A<Args...>, T<Args...>>>>
void func(const T<Args...>& t) {
cout<<"2"<<endl;
}
template <typename T>
void func(const T& t) {
cout<<"1"<<endl;
}
int main() {
B<int> b;
func(b);
func(5);
}
然而,这仅在 A
采用与 T
完全相同的模板参数时有效。因此,如果您的 B
更改为 ex。
template <typename T, typename U>
struct B : A<T> {}
这将不再有效。
解决方案 2
基于,您可以创建更通用的类型特征。此解决方案对其模板参数没有限制,就像解决方案 1 一样。
namespace detail
{
template <template <typename...> typename Base>
struct template_base_detector
{
template <typename... Args>
constexpr std::true_type operator()(Base<Args...>*);
constexpr std::false_type operator()(...);
};
}
template <template <typename...> typename Base, typename T>
struct is_template_base_of
: decltype(std::declval<detail::template_base_detector<Base>>()((T*)nullptr)) {};
// since C++ 14
template <template <typename...> typename Base, typename T>
constexpr bool is_template_base_of_v = is_template_base_of<Base, T>::value;
根据您的 c++ 版本,您可以使用不同的方法来利用此特性。
C++ 17
可能是最紧凑的解决方案。由于 C++ 17 constexpr if 语句是允许的,这允许我们只定义一个 func
:
template <typename T>
void func(const T& t)
{
if constexpr (is_template_base_of_v<A, T>)
cout << 2 << endl;
else
cout << 1 << endl;
}
C++ 11 和 14
我们必须退回到标签分发:
namespace detail
{
template <typename T>
void func(std::true_type, const T& t)
{
std::cout << 2 << endl;
}
template <typename T>
void func(std::false_type, const T& t)
{
std::cout << 1 << endl;
}
}
template <typename T>
void func(const T& t)
{
detail::func(is_template_base_of<A, T>{}, t);
}
代码调度。
首先,我们编写一个特征来检测是否有模板作为基础:
namespace details {
template<template<class...>class Z>
struct htb {
template<class...Ts>
constexpr std::true_type operator()(Z<Ts...>*){return {};}
constexpr std::false_type operator()(...){return {};}
};
}
template<template<class...>class Z, class X>
constexpr inline auto has_template_base = details::htb<Z>{}((X*)nullptr);
我们现在可以使用我们的新特征来标记调度:
namespace details{
template <typename T>
void func(std::true_type,const A<T>& t) {
std::cout<<"2"<<std::endl;
}
template <class T>
void func(std::false_type,const T& t) {
std::cout<<"1"<<std::endl;
}
}
template <typename T>
void func(const T& t) {
details::func(has_template_base<A,T>,t);
}
这是我的C++程序代码
#include <iostream>
#include <memory>
using namespace std;
template <typename T>
struct A { T x; };
template <typename T>
struct B:A<T> { };
template <typename T>
void func(const A<T>& t) {
cout<<"2"<<endl;
}
template <typename T>
void func(const T& t) {
cout<<"1"<<endl;
}
int main() {
B<int> b;
func(b);
}
并打印 1
。但我期望的是函数调用打印 2
。为什么 B<int>b
匹配 const T&
而不是 const A<T>&
。我怎样才能让它匹配 const A<T>&
?
解决方案 1
您可以使用 std::enable_if
和模板化模板参数来更好地匹配函数,例如:
#include <iostream>
#include <memory>
#include <type_traits>
using namespace std;
template <typename T>
struct A { T x; };
template <typename T>
struct B:A<T> { };
template <template <typename...> typename T, typename ...Args, typename = std::enable_if_t<std::is_base_of_v<A<Args...>, T<Args...>>>>
void func(const T<Args...>& t) {
cout<<"2"<<endl;
}
template <typename T>
void func(const T& t) {
cout<<"1"<<endl;
}
int main() {
B<int> b;
func(b);
func(5);
}
然而,这仅在 A
采用与 T
完全相同的模板参数时有效。因此,如果您的 B
更改为 ex。
template <typename T, typename U>
struct B : A<T> {}
这将不再有效。
解决方案 2
基于
namespace detail
{
template <template <typename...> typename Base>
struct template_base_detector
{
template <typename... Args>
constexpr std::true_type operator()(Base<Args...>*);
constexpr std::false_type operator()(...);
};
}
template <template <typename...> typename Base, typename T>
struct is_template_base_of
: decltype(std::declval<detail::template_base_detector<Base>>()((T*)nullptr)) {};
// since C++ 14
template <template <typename...> typename Base, typename T>
constexpr bool is_template_base_of_v = is_template_base_of<Base, T>::value;
根据您的 c++ 版本,您可以使用不同的方法来利用此特性。
C++ 17
可能是最紧凑的解决方案。由于 C++ 17 constexpr if 语句是允许的,这允许我们只定义一个 func
:
template <typename T>
void func(const T& t)
{
if constexpr (is_template_base_of_v<A, T>)
cout << 2 << endl;
else
cout << 1 << endl;
}
C++ 11 和 14
我们必须退回到标签分发:
namespace detail
{
template <typename T>
void func(std::true_type, const T& t)
{
std::cout << 2 << endl;
}
template <typename T>
void func(std::false_type, const T& t)
{
std::cout << 1 << endl;
}
}
template <typename T>
void func(const T& t)
{
detail::func(is_template_base_of<A, T>{}, t);
}
代码调度。
首先,我们编写一个特征来检测是否有模板作为基础:
namespace details {
template<template<class...>class Z>
struct htb {
template<class...Ts>
constexpr std::true_type operator()(Z<Ts...>*){return {};}
constexpr std::false_type operator()(...){return {};}
};
}
template<template<class...>class Z, class X>
constexpr inline auto has_template_base = details::htb<Z>{}((X*)nullptr);
我们现在可以使用我们的新特征来标记调度:
namespace details{
template <typename T>
void func(std::true_type,const A<T>& t) {
std::cout<<"2"<<std::endl;
}
template <class T>
void func(std::false_type,const T& t) {
std::cout<<"1"<<std::endl;
}
}
template <typename T>
void func(const T& t) {
details::func(has_template_base<A,T>,t);
}