c – 函数模板:extern模板与显式特化

考虑以下函数模板声明:

template <typename T, typename = std::enable_if_t<std::is_same_v<T, int>>>
void foo(T i);

该模板只有一个可能的有效实例化,即T = int.我想把这个定义放在一个实现文件中.我可以想到两种可能的方法. (如果你想知道为什么我会这样做而不只是说虚空foo(int i),那是因为模板版本阻止了在调用站点的隐式转换.)

方法1:

我可以使用extern模板声明告诉其他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

问题:

>这两种方法都合法,还是我无意中依赖UB?
>如果两者都是合法的,那么除了方法2的输入方式稍微少一点之外,是否有充分的理由选择一种方法而不是另一种方法? GCC和Clang两种方法同样适用.
>对于方法2,实际需​​要(*)的显式专业化声明吗?同样,GCC和Clang都非常高兴,如果它被省略,但这样做让我感到不安的是,在另一个TU中调用foo(3)是一个模板的隐式实例化,没有定义可见,并且没有这样的承诺其他地方存在定义.

解决方法:

还有第三种方法.在方法3中,您指定要使用的函数,并添加模板重载并将其标记为删除.看起来像

void foo(int i)
{
    // stuff
}

template <typename T>
void foo(T t) = delete;

由于模板版本将完全匹配所有类型,因此除了int之外在所有情况下都是首选,因为非模板完全匹配优先于模板1.所以你只能用int调用foo,所有其他类型都会给你一个错误,他们试图调用已删除的函数void foo(T t).

Live Example

上一篇:C++变量


下一篇:libcurl第二课 编译静态库