Warning: file_get_contents(/data/phpspider/zhask/data//catemap/6/cplusplus/154.json): failed to open stream: No such file or directory in /data/phpspider/zhask/libs/function.php on line 167

Warning: Invalid argument supplied for foreach() in /data/phpspider/zhask/libs/tag.function.php on line 1116

Notice: Undefined index: in /data/phpspider/zhask/libs/function.php on line 180

Warning: array_chunk() expects parameter 1 to be array, null given in /data/phpspider/zhask/libs/function.php on line 181

Warning: file_get_contents(/data/phpspider/zhask/data//catemap/3/templates/2.json): failed to open stream: No such file or directory in /data/phpspider/zhask/libs/function.php on line 167

Warning: Invalid argument supplied for foreach() in /data/phpspider/zhask/libs/tag.function.php on line 1116

Notice: Undefined index: in /data/phpspider/zhask/libs/function.php on line 180

Warning: array_chunk() expects parameter 1 to be array, null given in /data/phpspider/zhask/libs/function.php on line 181
C++ 在lib内部和外部专门化的模板类_C++_Templates_Static Libraries_Explicit Specialization - Fatal编程技术网

C++ 在lib内部和外部专门化的模板类

C++ 在lib内部和外部专门化的模板类,c++,templates,static-libraries,explicit-specialization,C++,Templates,Static Libraries,Explicit Specialization,考虑这个合成的例子。在VisualStudio 2010解决方案中,我有两个本地C++项目。一个是控制台exe,另一个是lib lib中有两个文件: // TImage.h template<class T> class TImage { public: TImage() { #ifndef _LIB std::cout << "Created (main), "; #else std::cout << "Created (lib),

考虑这个合成的例子。在VisualStudio 2010解决方案中,我有两个本地C++项目。一个是控制台exe,另一个是lib

lib中有两个文件:

// TImage.h

template<class T> class TImage
{
public:
  TImage()
  {
#ifndef _LIB
    std::cout << "Created (main), ";
#else
    std::cout << "Created (lib), ";
#endif
    std::cout << sizeof(TImage<T>) << std::endl;
  }

#ifdef _LIB
  T c[10];
#endif
};

void CreateImageChar();
void CreateImageInt();
那么这到底是怎么发生的,链接器用exe版本的
TImage
覆盖lib版本的
TImage
?但由于没有exe版本的
TImage
,它保留了lib版本的
TImage
?。。这种行为是否标准化,如果是,我在哪里可以找到描述


更新:下面给出的效果解释是正确的,谢谢。但问题是“这到底是怎么发生的”我希望得到一些链接器错误,比如“多重定义符号”。因此,最合适的答案来自。

内存布局是一个编译时概念;它与链接器无关。
main
函数认为TImage比
CreateImage…
函数小,因为它是用不同版本的TImage编译的

如果
CreateImage…
函数在标题中定义为内联函数,则它们将成为main.cpp编译单元的一部分,因此将报告与
main
报告相同的大小特征

这也与模板以及它们被实例化时无关。如果TImage是一个普通的班级,你也会观察到同样的行为


编辑:我刚刚注意到cout的第三行没有包含“Created(lib),10”。假设它不是打字错误,我怀疑发生的情况是,
CreateImageChar
没有内联调用构造函数,因此正在使用main.cpp的版本。

如果定义可用,编译器在使用模板时总是实例化模板

这意味着,它为所需的专门化生成所需的函数、方法等,并将它们放在对象文件中。这就是为什么您需要为正在使用的特定专门化提供定义(通常在头文件中)或现有实例化(例如,在另一个对象文件或库中)


现在,当链接时,可能会出现通常不允许的情况:每个类/函数/方法有多个定义。对于模板,这是特别允许的,编译器将为您选择一个定义。这就是在您的案例中发生的情况,您称之为“覆盖”。

模板代码创建重复的对象代码


编译器将复制您在实例模板时提供的类型的模板代码。因此,当编译
TImage.cpp
时,您会得到两个模板版本的目标代码,一个用于
TImage.o
中的char,另一个用于int。然后编译
main.cpp
,您将在
main.o
中获得char模板的新版本。然后链接器碰巧总是使用
main.o
中的链接器

这解释了为什么您的输出会产生“创建”行。但看到第3行和第4行中关于对象大小的不匹配有点令人费解:

Created (main), 1
10

这是由于编译器在编译期间解析了
sizeof
运算符。

模板在编译期间创建了类的副本(即空间)。因此,当您使用过多模板时,智能编译器会尝试基于模板参数化对其进行优化

在您的库中,您有一个
TImage
,它在构造时打印“lib”,并包含一个
T
数组。其中有两种,一种用于
int
,另一种用于
char

在main中,您有一个
TImage
,它在构造时打印“main”,并且不包含
T
的数组。在这种情况下,只有
char
版本;因为您从未要求创建
int
版本

当您转到link时,链接器选择两个
TImage
构造函数中的一个作为正式构造函数;它恰好选择了
main
的版本。这就是为什么第三行打印“main”而不是“lib”;因为您正在调用该版本的构造函数。通常,您不关心调用哪个版本的构造函数。。。它们都被要求是相同的,但您违反了该要求


这是最重要的部分:您的代码现在已被破坏。在库函数中,您希望在
TImage
中看到
char
数组,但构造函数从未创建它。此外,想象一下,如果您在
main
中说
new TImage
,并将指针传递到库中的某个函数,该函数将被删除
main
分配一个字节的空间,库函数尝试释放十个字节。或者如果您的
CreateImageChar
方法返回了它创建的
TImage
main
将在堆栈上为返回值分配一个字节,而
CreateImageChar
将用十个字节的数据填充它。依此类推。

我在这里假设您正在构建一个静态库,因为代码中没有任何
\u decelespec(dllexport)
外部“C”
。这里发生的事情如下。编译器为您的库创建
TImage
TImage
的实例。它还为您的可执行文件创建一个实例。当链接器将静态库和可执行文件的对象连接在一起时,重复的代码将被删除。请注意,静态库是以类似的对象代码链接的,因此,如果创建一个大的可执行文件或多个静态库和一个可执行文件,则没有任何区别。如果要构建一个可执行文件,结果将取决于对象链接的顺序;又名“未定义”

如果将库更改为DLL,则行为会更改。既然你是ca
// main.cpp

int _tmain(int argc, _TCHAR* argv[])
{
  TImage<char> image;
  std::cout << sizeof(TImage<char>) << std::endl;

  CreateImageChar();
  CreateImageInt();

  return 0;
}
// cout:
Created (main), 1
1
Created (main), 1
10
Created (lib), 40
40
Created (main), 1
10