考虑以下函数模板声明:
template <typename T, typename = std::enable_if_t<std::is_same_v<T, int>>>
void foo(T i);
此模板只有一个可能的有效实例化,即T = int
。我想把这个定义放在一个实现文件中。我可以想到两种可能的方法。 (如果您想知道为什么我会这样做而不只是说void foo(int i)
,那是因为模板版本会阻止在呼叫站点进行隐式转换。)
方法1:
我可以使用extern template
声明告诉其他TU foo<int>()
在其他地方实例化:
// In foo.hpp
template <typename T, typename = std::enable_if_t<std::is_same_v<T, int>>>
void foo(T i);
extern template void foo(int);
// In foo.cpp
template <typename T, typename>
void foo(T i) { ... } // full template definition
template void foo(int); // explicit instantiation with T = int
方法2:
我可以为int
案例提供明确的专业化:
// In foo.hpp
template <typename T, typename = std::enable_if_t<std::is_same_v<T, int>>>
void foo(T i);
template <> void foo(int i); // explicit specialisation declaration (*)
// In foo.cpp
template <>
void foo(int i) { ... } // explicit specialisation definition
问题:
(*)
实际需要明确的专业化声明吗?同样,GCC和Clang都非常高兴,如果它被省略,但这样做让我感到不安的是,在另一个TU中对foo(3)
的调用是模板的隐式实例化,没有可见的定义,并且没有承诺在其他地方存在这样的定义。答案 0 :(得分:6)
还有第三种方法。在方法3中,您可以指定要拥有的功能,并添加模板重载并将其标记为delete
。看起来像是
void foo(int i)
{
// stuff
}
template <typename T>
void foo(T t) = delete;
由于模板版本将完全匹配所有类型,因此除int
之外的所有情况都将首选,因为非模板完全匹配优先于模板1。因此,您只能使用foo
拨打int
,而其他所有类型都会向您发送错误信息,表明他们正在尝试调用已删除的函数void foo(T t)
。