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

Warning: file_get_contents(/data/phpspider/zhask/data//catemap/2/csharp/324.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++ 为什么可以在.h接口和.cpp实现中拆分非模板类?_C++_Templates - Fatal编程技术网

C++ 为什么可以在.h接口和.cpp实现中拆分非模板类?

C++ 为什么可以在.h接口和.cpp实现中拆分非模板类?,c++,templates,C++,Templates,我理解,如果您试图在.h接口和.cpp实现中拆分模板类,则会出现链接器错误。正如一篇流行文章中提到的,这样做的原因是“如果实现不在头中,它们将无法访问,因此编译器将无法实例化模板。” 我不明白的是,如果一个.cpp文件中的实现在模板类的情况下是不可访问的,那么对于非模板类或普通类,是什么使它们可以访问呢。为什么我们能够在一个.h和.cpp文件上拆分普通类的接口和实现,而不会出现链接器错误 测试.h template<typename TypeOne> TypeOne ProcessV

我理解,如果您试图在.h接口和.cpp实现中拆分模板类,则会出现链接器错误。正如一篇流行文章中提到的,这样做的原因是“如果实现不在头中,它们将无法访问,因此编译器将无法实例化模板。”

我不明白的是,如果一个.cpp文件中的实现在模板类的情况下是不可访问的,那么对于非模板类或普通类,是什么使它们可以访问呢。为什么我们能够在一个.h和.cpp文件上拆分普通类的接口和实现,而不会出现链接器错误

测试.h

template<typename TypeOne>
TypeOne ProcessVal(TypeOne val);

此代码给出链接器错误。类似的非模板类拆分不会导致链接器错误。我可以发布代码,但你明白了。

当你有一个模板定义时,编译单元中没有添加任何内容,因为模板参数可能是很多东西,所以你无法从编译时知道要创建什么类,如答案中所述


对于非模板化的情况,你知道你的类中有什么,你不必等待模板参数的给出来真正生成实际的类,因此链接器可以看到它们(因为它们被编译成doc's post所说的二进制文件)。

当你有一个模板定义时,编译单元中没有添加任何内容,因为模板参数可能有很多内容,所以您无法从编译时就知道要创建什么类,如答案中所述


对于非模板化的情况,你知道你的类中有什么,你不必等待模板参数的给出来真正生成实际的类,因此链接器可以看到它们(因为它们被编译成doc's post所说的二进制文件)。

对于普通函数,编译器直接生成代码,并将生成的代码添加到编译单元

测试.cpp

template<typename TypeOne>
TypeOne ProcessVal(TypeOne val)
{
    // Process it here.
    return val;
}
void main()
{
    int a, b;
    b = ProcessVal(a);
}
int ProcessVal(int val)
{
    // Process it here.
    return val;
}
template<typename TypeOne>
TypeOne ProcessVal(TypeOne val)
{
    // Process it here.
    return val;
}

在上述代码的情况下,所有必要的信息都是已知的,函数的C++代码“程序>进程/<代码>可以翻译成机器指令。因此,对象文件(可能称为

Test.o
)将包含
ProcessVal
symbol+相应的代码,链接器可以引用它(以生成调用或执行内联)

另一方面,这段代码:

测试.cpp

template<typename TypeOne>
TypeOne ProcessVal(TypeOne val)
{
    // Process it here.
    return val;
}
void main()
{
    int a, b;
    b = ProcessVal(a);
}
int ProcessVal(int val)
{
    // Process it here.
    return val;
}
template<typename TypeOne>
TypeOne ProcessVal(TypeOne val)
{
    // Process it here.
    return val;
}
模板
TypeOne进程val(TypeOne val)
{
//在这里处理。
返回val;
}

不向编译单元提供任何输出。此编译单元的对象文件(
Test.o
)将不包含
ProcessVal()
函数的任何代码,因为编译器不知道
TypeOne
参数将是什么类型。您必须实例化模板以获得其二进制形式,并且只能将其添加到生成的二进制中。

对于普通函数,编译器会立即生成代码,并将生成的代码添加到编译单元中

测试.cpp

template<typename TypeOne>
TypeOne ProcessVal(TypeOne val)
{
    // Process it here.
    return val;
}
void main()
{
    int a, b;
    b = ProcessVal(a);
}
int ProcessVal(int val)
{
    // Process it here.
    return val;
}
template<typename TypeOne>
TypeOne ProcessVal(TypeOne val)
{
    // Process it here.
    return val;
}

在上述代码的情况下,所有必要的信息都是已知的,函数的C++代码“程序>进程/<代码>可以翻译成机器指令。因此,对象文件(可能称为

Test.o
)将包含
ProcessVal
symbol+相应的代码,链接器可以引用它(以生成调用或执行内联)

另一方面,这段代码:

测试.cpp

template<typename TypeOne>
TypeOne ProcessVal(TypeOne val)
{
    // Process it here.
    return val;
}
void main()
{
    int a, b;
    b = ProcessVal(a);
}
int ProcessVal(int val)
{
    // Process it here.
    return val;
}
template<typename TypeOne>
TypeOne ProcessVal(TypeOne val)
{
    // Process it here.
    return val;
}
模板
TypeOne进程val(TypeOne val)
{
//在这里处理。
返回val;
}

不向编译单元提供任何输出。此编译单元的对象文件(
Test.o
)将不包含
ProcessVal()
函数的任何代码,因为编译器不知道
TypeOne
参数将是什么类型。您必须实例化模板以获得其二进制形式,并且只能将其添加到生成的二进制中。

基本上,这可以追溯到旧的C语言。
.h
文件是打算用C预处理器完成的,并且由预处理器以文本的形式包含到C源代码中。所以如果你有
foo.h

int i = 0;
foo.c

#include "foo.h"
int main(){ printf("%d\n", i);}
当预处理器完成时,编译器实际上看到:

int i = 0;
int main(){ printf("%d\n", i);}
作为源文件。不可能发生链接器错误,因为链接器从未涉及——您只编译了一个C文件


虽然现在模板的语义稍微复杂一些,但编程模型仍然是一样的:您的
.h
文件包括在实际的最终解析和编译发生之前从词汇上引入程序的程序文本。

基本上,这可以追溯到旧的C语言。
.h
文件是打算用C预处理器完成的,并且由预处理器以文本的形式包含到C源代码中。所以如果你有
foo.h

int i = 0;
foo.c

#include "foo.h"
int main(){ printf("%d\n", i);}
当预处理器完成时,编译器实际上看到:

int i = 0;
int main(){ printf("%d\n", i);}
作为源文件。不可能发生链接器错误,因为链接器从未涉及——您只编译了一个C文件


虽然现在模板的语义稍微复杂了一些,但编程模型仍然是一样的:您的
.h
文件包含从词汇上引入程序的程序文本,在实际的最终解析和编译之前,

如果你真的想在一个单独的C++文件中实现模板函数或类,你可以针对某个类型。例如,在您的特定示例中,如果将此行添加到
test.cpp
,您的代码将成功链接

模板int-ProcessVal(int-val);

如果你真的想在一个单独的C++文件中实现模板函数或类,你可以