我有这个头文件:
Utility.h:
#pragma once
#include <fstream>
#include <iostream>
#include <string>
#include <vector>
#include <Windows.h>
using namespace std;
template<std::size_t> struct int_ {};
template <class Tuple, size_t Pos>
std::ostream& print_tuple(std::ostream& out, const Tuple& t, int_<Pos>);
struct Timer {
public:
Timer();
void start();
double getMilliSec();
private:
LARGE_INTEGER frequency; // ticks per second
LARGE_INTEGER t1, t2; // ticks
};
//...
#include "Utility.cpp"
这个实现文件:
Utility.cpp:
#include "Utility.h"
template <class Tuple, size_t Pos>
std::ostream& print_tuple(std::ostream& out, const Tuple& t, int_<Pos>) {
...
}
Timer::Timer() {
// get ticks per second
QueryPerformanceFrequency(&frequency);
}
void Timer::start() {
QueryPerformanceCounter(&t1);
}
double Timer::getMilliSec() {
QueryPerformanceCounter(&t2);
return (t2.QuadPart - t1.QuadPart) * 1000.0 / frequency.QuadPart;
}
返回此错误:
error C2995: 'std::ostream &print_tuple(std::ostream &,const Tuple &,int_<Pos>)': function template has already been defined
error C2084: function 'Timer::Timer(void)' already has a body
...
问题不在于警卫(正如this问题所示),因为我使用#pragma once
我已经了解了implementing .tpp files的模板类实现,但我发现这是一个可怕的解决方案,因为Visual Studio将不会格式化此文件。
尝试定义Utility.tpp :(错误解决方案)
所以我在#include "Utility.cpp
中用#include "Utility.tpp
替换了Utility.h
并定义了......
Utility.tpp
Timer::Timer() {
// get ticks per second
QueryPerformanceFrequency(&frequency);
}
void Timer::start() {
QueryPerformanceCounter(&t1);
}
double Timer::getMilliSec() {
QueryPerformanceCounter(&t2);
return (t2.QuadPart - t1.QuadPart) * 1000.0 / frequency.QuadPart;
}
现在返回的错误是:
1>Memoization.obj : error LNK2005: "public: __cdecl Timer::Timer(void)" (??0Timer@@QEAA@XZ) already defined in HelloWorld.obj
...
这是主要的顺便说一句:
HelloWorld.cpp:
#include <algorithm>
#include <stdio.h>
#include <stdlib.h>
#include <time.h>
//#include "Memoization.cpp"
#include<vector>
#include"Utility.h"
using namespace std;
int main()
{
}
尝试定义Utility.tpp :(正确的解决方案)
正如在评论中注意到的那样,作为一个白痴,我在Time
文件中导入了.tpp
方法,显然我应该导入模板函数,因此Utility.tpp
包含print_tuple
,Utility.cpp
包含3 Timer
个函数。
答案 0 :(得分:3)
如果要将cpp文件包含到头文件中,则需要在项目中排除该cpp文件。如果不这样,那么头文件将具有cpp文件所具有的内容,并且当编译器将cpp文件编译为对象时,您将拥有两个定义副本。一个在头文件中,因为它有一个cpp文件的副本,一个在cpp文件中,因为它实际上是编译的。这会导致重新定义错误。
一旦你删除了Utility.cpp,那么包含Utility.h的任何其他cpp文件都会有一个完整的源代码,因为还包含了Utility.cpp。
要从编译中删除Utility.cpp,请参阅:How to exclude files from Visual Studio compile?
如果你使用cpp文件的唯一原因是因为tpp文件没有将C ++代码格式化为C ++,那么你可以配置MSVS将tpp文件视为C ++文件。如果你去工具 - &gt;选项 - &gt;文字编辑器 - &gt;文件扩展名您可以为格式添加文件扩展名。在扩展框中输入扩展名,从编辑器下拉列表中选择Microsoft Visual C ++,然后单击“添加”。
现在您可以使用tpp文件而不是cpp文件,而不必记住从构建中排除cpp文件。你也可以遵守现代实践。
答案 1 :(得分:0)
主要有两个问题:
您已将预期的标题代码放入&#34; .cpp&#34;文件,默认情况下,您的IDE将其视为主要源代码文件(翻译单元)。
您在标头中的全局命名空间中包含非inline
函数定义(类成员函数)。当该标头包含在两个或多个翻译单元中时,您将获得一个定义规则(ODR)违规。在实践中,链接器会抱怨。
解决此问题:
从&#34; .cpp&#34;更改文件扩展名例如&#34; .HPP&#34;或者只是简单的&#34; .h&#34;。它是用于头文件使用的代码。文件扩展名应该反映出来,而不是误导。
声明函数inline
,或将定义放在类定义中。
在其他新闻中,标准库中的<chrono>
个时钟之一可能会满足您的目的,因此您不必在标题中包含<windows.h>
。它确实拖累了无数不合理的宏。默认情况下,包含小写min
和max
宏,因此此代码非常不合适。
答案 2 :(得分:-1)
删除#include&#34; Utility.cpp&#34;来自Utility.h的一行。 然后再次编译Utility.cpp