功能......已经是一个身体和一个功能模板已经定义

时间:2016-05-02 12:56:10

标签: c++ templates header-files

我有这个头文件:

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_tupleUtility.cpp包含3 Timer个函数。

3 个答案:

答案 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>。它确实拖累了无数不合理的宏。默认情况下,包含小写minmax宏,因此此代码非常不合适。

答案 2 :(得分:-1)

删除#include&#34; Utility.cpp&#34;来自Utility.h的一行。 然后再次编译Utility.cpp