链接模板目标文件 - clang 和 gcc 的不同行为
Linking of template object files - different behavior with clang and gcc
关于 gcc 和 clang 之间不同行为的问题很多。但是我还没有找到解决问题的方法。
我使用模板,我想传播 class 的定义和实现。我读过(很多)我知道不同的可能性。我选择特定的声明,我想支持哪些模板。有:
template class Temp<char>;
template class Temp<double>;
如果我将这些行放在 template.cc 文件的末尾,我会选择我支持的模板。但是使用 gcc 我也可以将它写在头文件中。 Clang 不支持它,我收到链接错误。
但是为什么呢?头文件中使用模板的声明有什么问题???
这是我的玩具示例:
template.hh
#pragma once
#include <iostream>
template<typename T>
class Temp
{
public:
Temp(T data);
virtual ~Temp (){};
void print();
private:
T data_;
};
//Only gcc can support this
//template class Temp<char>;
//template class Temp<double>
template.cc
#include "template.hh"
template<typename T>
Temp<T>::Temp(T data): data_(data)
{
}
template<typename T>
void Temp<T>::print()
{
std::cout << data_ << " " << sizeof(data_) << std::endl;
}
//Delete those, if it is used in header
template class Temp<char>;
template class Temp<double>;
test.cc
#include "template.hh"
int main(int argc, char *argv[])
{
Temp<char> temp = Temp<char>('c');
temp.print();
Temp<double> temp2 = Temp<double>(1.0);
temp2.print();
return 0;
}
期望的输出:
c 1
1 8
显式模板实例化属于“.cc”实现文件,而不是 header。如果你想在 header 中声明它,你可以使用 extern
:
extern template class Temp<char>;
extern template class Temp<double>;
这将避免您在使用 Clang 时可能遇到的 multiply-defined 符号。
可能是 GCC 在 header 文件中支持显式实例化,但这并不意味着它是正确的 C++,只是在这种情况下 GCC 是自由的。不要依赖那个。
函数模板的显式实例化是一个定义。 class模板的显式实例化是short-hand,用于实例化class模板的所有[non-template]成员函数。在 C++ 中对一个实体进行多个定义会导致违反 ODR-rule(一个定义规则)。违反ODR-rule不需要诊断。
因此,模板的显式实例化属于“.cc”文件。如果你想声明模板将在某个翻译单元中显式实例化,你可以使用extern
模板声明,例如:
template <typename> void f();
template <typename> class F { /*...*/ };
extern template void f<int>();
extern template class F<int>();
extern
模板声明允许在 header 中定义模板,但禁止隐式实例化。相反,extern
模板声明承诺将有一个翻译单元提供显式实例化。
关于 gcc 和 clang 之间不同行为的问题很多。但是我还没有找到解决问题的方法。
我使用模板,我想传播 class 的定义和实现。我读过(很多)我知道不同的可能性。我选择特定的声明,我想支持哪些模板。有:
template class Temp<char>;
template class Temp<double>;
如果我将这些行放在 template.cc 文件的末尾,我会选择我支持的模板。但是使用 gcc 我也可以将它写在头文件中。 Clang 不支持它,我收到链接错误。
但是为什么呢?头文件中使用模板的声明有什么问题???
这是我的玩具示例:
template.hh
#pragma once
#include <iostream>
template<typename T>
class Temp
{
public:
Temp(T data);
virtual ~Temp (){};
void print();
private:
T data_;
};
//Only gcc can support this
//template class Temp<char>;
//template class Temp<double>
template.cc
#include "template.hh"
template<typename T>
Temp<T>::Temp(T data): data_(data)
{
}
template<typename T>
void Temp<T>::print()
{
std::cout << data_ << " " << sizeof(data_) << std::endl;
}
//Delete those, if it is used in header
template class Temp<char>;
template class Temp<double>;
test.cc
#include "template.hh"
int main(int argc, char *argv[])
{
Temp<char> temp = Temp<char>('c');
temp.print();
Temp<double> temp2 = Temp<double>(1.0);
temp2.print();
return 0;
}
期望的输出:
c 1
1 8
显式模板实例化属于“.cc”实现文件,而不是 header。如果你想在 header 中声明它,你可以使用 extern
:
extern template class Temp<char>;
extern template class Temp<double>;
这将避免您在使用 Clang 时可能遇到的 multiply-defined 符号。
可能是 GCC 在 header 文件中支持显式实例化,但这并不意味着它是正确的 C++,只是在这种情况下 GCC 是自由的。不要依赖那个。
函数模板的显式实例化是一个定义。 class模板的显式实例化是short-hand,用于实例化class模板的所有[non-template]成员函数。在 C++ 中对一个实体进行多个定义会导致违反 ODR-rule(一个定义规则)。违反ODR-rule不需要诊断。
因此,模板的显式实例化属于“.cc”文件。如果你想声明模板将在某个翻译单元中显式实例化,你可以使用extern
模板声明,例如:
template <typename> void f();
template <typename> class F { /*...*/ };
extern template void f<int>();
extern template class F<int>();
extern
模板声明允许在 header 中定义模板,但禁止隐式实例化。相反,extern
模板声明承诺将有一个翻译单元提供显式实例化。