我有以下代码:
#include <iostream>
class A
{};
class B
{};
template<typename T>
void Do(T data)
{
std::cout << "Do() defaultn";
}
template<>
void Do(A* data)
{
std::cout << "Do(A*)n";
}
template<>
void Do(B* data)
{
std::cout << "Do(B*)n";
}
int main(int argc, char* argv[])
{
A* a = nullptr;
B* b = nullptr;
const A* aConst = nullptr;
const B* bConst = nullptr;
Do(a);
Do(aConst);
Do(b);
Do(bConst);
return 0;
}
输出:
Do(A*)
Do() default
Do(B*)
Do() default
我应该如何重写代码以共享const&非const类型,无需复制粘贴带有const关键字说明符的专业化,从而生成输出:
Do(A*)
Do(A*)
Do(B*)
Do(B*)
您可以重载,而不是专门化。使用
template<typename T, std::enable_if_t<std::is_same_v<std::decay_t<T>, A>, bool> = true>
void Do(T* data)
{
std::cout << "Do(A*)n";
}
template<typename T, std::enable_if_t<std::is_same_v<std::decay_t<T>, B>, bool> = true>
void Do(T* data)
{
std::cout << "Do(B*)n";
}
传递const A*
/A*
/const B*
/B*
时将调用这些函数,因为它们比通用模板更匹配。它们是更好匹配的原因是T
更受约束。它被认为是更专业的,因此它将在过载解决中与通用模板的平局中获胜。
C++14
我对这个答案投了赞成票,这对SFINAE来说是一个很好的展示。要对其进行扩展,可以使用std::is_convertible
简化SFINAE表达式。这更类似于重载解析的工作方式(添加const限定符(。
template<typename T, std::enable_if_t<std::is_convertible_v<T*, const A*>, int> = 0>
void Do(T* data)
{
std::cout << "Do(A*)n";
}
template<typename T, std::enable_if_t<std::is_convertible_v<T*, const B*>, int> = 0>
void Do(T* data)
{
std::cout << "Do(B*)n";
}
C++17
另外,在带有constexpr if
的C++17中,您可以在所有情况下使用一个函数:
template<typename T>
void Do(T data)
{
if constexpr (std::is_convertible_v<T, const A*>)
std::cout << "Do(A*)n";
else if constexpr (std::is_convertible_v<T, const B*>)
std::cout << "Do(B*)n";
else
std::cout << "Do() defaultn";
}
如果你愿意写一个小存根,你可以使用这个模式:
template<>
void Do(const A* data)
{
std::cout << "Do(A*)n";
}
template<>
void Do(A* data)
{
Do((const A*)data);
}
您不想复制的主代码使用const A*
,因为您希望它也能处理常量数据。非常简单地转发给它。