2013-06-30 149 views
2

我正在阅读文章An Idiot's Guide to C++ Templates - Part 2并来到Separation of Declaration and Implementation部分。在头文件中包含模板实现cpp文件和链接混淆

现在我有三个文件及其内容如下:

sample.hpp

#ifndef SAMPLE_HPP 
#define SAMPLE_HPP 

template <typename T> 
void displayValue(T tValue); 

#include "sample.cpp" 

#endif 

sample.cpp的

#include <iostream> 
template <typename T> 
void displayValue(T tValue){ 
    std::cout<<tValue<<std::endl; 
} 

的main.cpp

#include "sample.hpp" 
int main(void) { 
    displayValue(20); 
    displayValue(3.14); 
    return 0; 
} 

据笔者,

项目/现在打造不得添加sample.cpp的编译过程

但是相反,当我使用:

g++ main.cpp sample.cpp -o main 

它仍然有效!

我认为在这种情况下,对象sample.o仍然包含NO代码有关模板功能displayValue,并在main.o对象,它包含的内容。所以理论上没有错误。但为什么作者说must not

+0

作者当然不是只谈论gcc,我们也不能说其他编译器会如何表现。如果你再次链接之前链接的东西会出现错误 –

+0

@MaduraAnushanga是的,我还注意到作者正在使用MSVC编译器。我只需要确认,如果我们错误地将'sample.cpp'添加到编译文件中并且不会发生链接错误,那么就没有任何危害。如果作者只谈论MSVC,那么我猜这是编译器的一个缺陷;但我不确定。 –

+1

这取决于你使用的编译器,作者已经说明了最佳实践。我更喜欢在头文件中不包含cpps,您可能刚刚在头文件中实现了它的功能,但它的工作原理可能会有问题。 –

回答

0

模板是部分定义,因此它们可以被限定为“部分”实现在标题中。在你的情况,

sample.hpp

#ifndef SAMPLE_HPP 
#define SAMPLE_HPP 

#include <iostream> 
template <typename T> 
void displayValue(T tValue){ 
    std::cout<<tValue<<std::endl; 
} 
#endif 

为主。CPP

#include "sample.hpp" 
int main(void) { 
    displayValue(20); 
    displayValue(3.14); 
    return 0; 
} 

这将在任何情况下完美地工作,即使你在多个位置添加标题(和非常具有可读性)

否则你仍然可以让你做了什么,是“理论上正确“通过将sample.cpp包含在使用该模板的其他cpp中。这是一个更多的工作。

N.B:这不是问题的确切答案,而是一种绕过所有问题的方法。

1

这是正常的,你没有得到任何错误。因为你的定义和实现被视为一个文件。

更好和平常的C++风格是;

头文件(myClass.h): 您不应该包含实现文件(编译器会在您找到任何文件时找到它)。 实施(myClass.cpp): 的#include myClass.h

主程序(main.cpp中) 这也将需要的#include myClass.h

如果实现使用这种一贯的风格您只会因为不应该分开模板函数/类的定义和实现而导致链接错误。

作者可能指的是这个。

+0

作者在通常的分离案例中说,那里将会是一个链接错误,因为函数模板和声明的实现在两个分离的翻译单元中,并且由于没有实例化,所以实际上将被丢弃。然后作者说这可以用这种不寻常的方式解决。所以作者对“不得”声明犯了错误?最有可能的是 –

+1

。请注意,预处理器只是将**#include **指令替换为文件的内容。因此,实际上这种不同寻常的风格,作者正在同一个文件中提供定义和实现。 – sgun