众所周知,当我们想要在头文件中声明模板化类/函数并在源 cpp 文件中定义它们时,必须在所述 cpp 文件的末尾添加显式初始化 - 限制是,那么,我们的模板将仅适用于明确定义的情况。
例如,假设我们有以下标头,其中包含一个模板化类,该类也具有模板化成员函数:
// header classA.h
template<class T>
class ClassA
{
/* template<class U> */
/* friend class ClassA; */
public:
template<typename U>
void foo(U val);
};
extern template class ClassA<double>;
extern template void ClassA<double>::foo(double);
这里,
extern
告诉编译器,每当包含此标头时,编译器不应尝试在此处初始化 ClassA 或其成员函数 - 定义将出现在其他地方。
在我们的例子中,它们来自以下 cpp 源文件:
// header classA.cpp
#include <stdio.h>
#include "classA.h"
template<class T>
template<typename U>
void ClassA<T>::foo(U val)
{
// other code that uses val goes here
printf("foo\n");
}
template class ClassA<double>;
template void ClassA<double>::foo(double);
我们用这个 main.cpp 测试代码:
#include <stdio.h>
#include "classA.h"
void test(double x) { printf("test\n"); }
int main()
{
ClassA<int> a;
float x = 1.0; // this being a float throws a linker error; it must be double
a.foo(x);
test(x);
return 0;
}
上面的代码无法编译,因为
x
是 float
。 ld
链接器(使用 G++ 12.3.0)抛出链接错误:
/usr/bin/ld: /tmp/ccVYWPa2.o: 在函数中
void ClassA::foo(float)'collect2: 错误: ld 返回 1 退出 status bash: ./a.out: 没有这样的文件或目录main': main.cpp:(.text.startup+0x26): undefined reference to
如果我们将
x
更改为 double
,则程序将按预期编译。也就是说,当 x
为 float
时,从 float 到 double 的隐式转换不会启动。我不明白的是为什么。当然,一般来说编译器不能明确地决定模板化参数的类型。但是由于给出了显式初始化 - 在这个简单的例子中只有一个 - 我希望编译器在传递给 foo
时隐式地将浮点转换为双精度 - 因为没有其他选项被显式初始化以产生歧义.
也就是说,当显式初始化更多情况时,我当然可以理解歧义:
template class ClassA<double>;
template void ClassA<double>::foo(double);
template class ClassA<double>;
template void ClassA<double>::foo(long double);
所以,我的问题有两个:
如果没有显式初始化不明确的情况,为什么隐式转换没有发生?
在这样的例子中,有没有办法强制隐式转换至少当
T
=U
时(即可能基于T
,类似于类似但不同情况的答案)?
float
匹配 U
,然后它正在寻找不存在的定义。T
作为函数参数,因此该函数不是模板化的(只是它所属的类)。然后 a.foo
将期望一个 int
,这将导致 a.foo(x)
进行隐式转换。然后它就无法链接,因为您只提供了 double
版本。