CPP参考 https://en.cppreference.com/w/cpp/types/enable_if:
一个常见的错误是声明两个仅不同的函数模板
在他们的默认模板参数中。这不起作用,因为
声明被视为同一函数的重新声明
模板(函数中不考虑默认模板参数
模板等价)。
/*** WRONG ***/
struct T {
enum { int_t,float_t } m_type;
template <typename Integer,
typename = std::enable_if_t<std::is_integral<Integer>::value>
>
T(Integer) : m_type(int_t) {}
template <typename Floating,
typename = std::enable_if_t<std::is_floating_point<Floating>::value>
>
T(Floating) : m_type(float_t) {} // error: treated as redefinition
};
/* RIGHT */
struct T {
enum { int_t,float_t } m_type;
template <typename Integer,
std::enable_if_t<std::is_integral<Integer>::value, int> = 0
>
T(Integer) : m_type(int_t) {}
template <typename Floating,
std::enable_if_t<std::is_floating_point<Floating>::value, int> = 0
>
T(Floating) : m_type(float_t) {} // OK
};
在模板类型中使用enable_if时应小心
命名空间范围函数模板的非类型参数。一些 ABI
像 Itanium ABI 这样的规范不包括
非类型模板参数的实例化相关部分
mangling,意味着两个不同函数的专业化
模板可能最终会出现相同的错误名称并且出现错误
链接在一起。例如:
// first translation unit
struct X {
enum { value1 = true, value2 = true };
};
template<class T, std::enable_if_t<T::value1, int> = 0>
void func() {} // #1
template void func<X>(); // #2
// second translation unit
struct X {
enum { value1 = true, value2 = true };
};
template<class T, std::enable_if_t<T::value2, int> = 0>
void func() {} // #3
template void func<X>(); //#4
函数模板 #1 和 #3 具有不同的签名,并且是
独特的模板。尽管如此,#2和#4,尽管是
不同函数模板的实例化,具有相同的损坏
Itanium C++ ABI 中的名称 (_Z4funcI1XLi0EEvv),表示
链接器会错误地认为它们是同一实体。
Fix:
#include <iostream>
#include <type_traits>
// Enable if "T" is integral
template <typename T,
std::enable_if_t<std::is_integral_v<T>, int> = 0
>
void print(T value)
{
std::cout << "Integral: " << value << std::endl;
}
// Enable if "T" is not integral
template <typename T,
std::enable_if_t<!std::is_integral_v<T>, int> = 0
>
void print(T value)
{
std::cout << "Not Integral: " << value << std::endl;
}
int main()
{
int i = 42;
print(i);
double d = 42.0;
print(d);
}
更好的是,使用概念:
#include <iostream>
#include <concepts>
// Enable if "T" is integral
template <std::integral T>
void print(T value)
{
std::cout << "Integral: " << value << std::endl;
}
// Enable if "T" is not integral
template <typename T>
void print(T value)
{
std::cout << "Not Integral: " << value << std::endl;
}
int main()
{
int i = 42;
print(i);
double d = 42.0;
print(d);
}
LIVE https://gcc.godbolt.org/z/X8NRZb