功能。。。已经定义了主体和功能模板

function ... has already a body & function template has already been defined

提问人:justHelloWorld 提问时间:5/2/2016 最后编辑:CommunityjustHelloWorld 更新时间:12/1/2018 访问量:2537

问:

我有这个头文件:

实用程序.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"

而这个实现文件:

实用程序.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
...

问题不在于守卫(如问题中所建议的),因为我使用#pragma once

我已经阅读了为模板类实现实现 .tpp 文件的信息,但我发现这是一个可怕的解决方案,因为 Visual Studio 不会从此文件中格式化任何内容。

尝试定义 Utility.tpp:(错误的解决方案)

所以我用 in 代替并定义......#include "Utility.cpp#include "Utility.tppUtility.h

实用程序.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:(正确的解决方案,最后)

正如评论中让我注意到的那样,作为一个白痴,我导入了文件中的方法,显然我应该导入模板函数,所以在包含的同时包含 3 个函数。Time.tppUtility.tppprint_tupleUtility.cppTimer

C++ 模板 头文件

评论

0赞 NathanOliver 5/2/2016
头文件中有什么作用?#include "Utility.cpp"
0赞 justHelloWorld 5/2/2016
这里是方法 3 或这里是“一个常见的解决方案...”
1赞 Pete Becker 5/2/2016
嗯,是的,“一个常见的解决方案”,但这个代码没有的问题的解决方案。这里没有模板。
0赞 justHelloWorld 5/2/2016
查看更新后的答案;)
1赞 Quentin 5/2/2016
@justHelloWorld......修复您的 IDE ?

答:

-1赞 dutchdukes 5/2/2016 #1

从 Utility.h 中删除 #include“Utility.cpp”行。 然后再次编译实用程序 .cpp

评论

0赞 Cheers and hth. - Alf 5/2/2016
−1如果不对函数模板执行某些操作,则无法工作。
3赞 NathanOliver 5/2/2016 #2

如果要将 cpp 文件包含在头文件中,则需要将该 cpp 文件排除在项目中的编译之外。如果没有,则头文件将具有 cpp 文件所具有的内容,当编译器将 cpp 文件编译为对象时,您将拥有定义的两个副本。一个在头文件中,因为它中有 cpp 文件的副本,另一个在 cpp 文件中,因为这是实际编译的内容。这会导致重新定义错误。

从编译中删除 Utility.cpp 后,任何其他包含 Utility.h 的 cpp 文件都将具有完整源代码的副本,因为 Utility.cpp 也包括在内。

若要从编译中删除实用工具 .cpp,请参阅:如何从 Visual Studio 编译中排除文件?


如果使用 cpp 文件的唯一原因是 tpp 文件不将 C++ 代码格式化为 C++,则可以将 MSVS 配置为将 tpp 文件视为 C++ 文件。如果转到“工具”->“选项”-“>”文本编辑器“-”文件扩展名“>,则可以在格式中添加文件扩展名。在扩展框中输入扩展,从编辑器下拉列表中选择“Microsoft Visual C++”,然后单击“添加”。

现在,您可以使用 tpp 文件而不是 cpp 文件,并且不必记住从构建中排除 cpp 文件。您还可以遵守现代惯例。

0赞 Cheers and hth. - Alf 5/2/2016 #3

主要有两个问题:

  • 您已将预期的头代码放在“.cpp”文件中,默认情况下,IDE 会将其视为主源代码文件(转换单元)。

  • 您将非函数定义(类成员函数)包含在标头的全局命名空间中。当该标头包含在两个或多个翻译单元中时,您将收到一个定义规则 (ODR) 冲突。在实践中,链接者会抱怨。inline

要解决此问题,请执行以下操作:

  • 将文件扩展名从“.cpp”更改为例如“.hpp”或只是普通的“.h”。它是用于头文件的代码。文件扩展名应该反映这一点,而不是误导。

  • 声明函数,或将定义放在类定义中。inline


在其他新闻中,可能来自标准库中的一个时钟将达到您的目的,这样您就不必包含在标题中。它确实拖入了无数不合理的宏。默认情况下,包括小写字母和宏,因此此代码非常不好。<chrono><windows.h>minmax