与常规函数不同,模板函数可能仅通过返回类型重载。
template <typename T> int f() { return 1; }
template <typename T> long f() { return 2; }
int main() {
int (&f1) () = f<void>;
long (&f2) () = f<void>;
return f1() == f2();
}
这里,假设一个非优化编译器,生成的程序集将包含两个函数f<void>(),但它们不能共享相同的错位名称,否则main 的生成程序集将无法指定它指的是哪个实例化。
通常,如果您有一个重载的模板函数,则只有一个定义将用于特定的模板参数,所以这并不常见,但在 Columbo 的回答中,dyp 提出了如何实现这一点的基本想法可能实际上很有用。在Can addressof() be implemented as constexpr function?,我想出了
template <bool>
struct addressof_impl;
template <>
struct addressof_impl<false> {
template <typename T>
static constexpr T *impl(T &t) {
return &t;
}
};
template <>
struct addressof_impl<true> {
template <typename T>
static /* not constexpr */ T *impl(T &t) {
return reinterpret_cast<T *>(&const_cast<char &>(reinterpret_cast<const volatile char &>(t)));
}
};
template <typename T>
constexpr T *addressof(T &t)
{
return addressof_impl<has_overloaded_addressof_operator<T>::value>::template impl<T>(t);
}
但是,如果在多个翻译单元中使用相同的实例化 addressof<X>,这实际上是违反 ODR 的,有些 X 不完整,有些 X 完整并且具有重载的 & 运算符。这可以通过使用常规重载函数直接执行addressof 内部的逻辑来重新处理。
template <typename T>
std::enable_if_t<has_overloaded_addressof_operator<T>::value, T *>
addressof(T &t)
{
return reinterpret_cast<T *>(&const_cast<char &>(reinterpret_cast<const volatile char &>(t)));
}
template <typename T>
constexpr
std::enable_if_t<!has_overloaded_addressof_operator<T>::value, T *>
addressof(T &t)
{
return &t;
}
(出于同样的原因,has_overloaded_addressof_operator 也需要内联。)
这样就避免了问题:当X 不完整时,addressof<X> 所指的函数与X 完整时不同。