function ... 已经定义了函数体和函数模板
function ... has already a body & function template has already been defined
我有这个头文件:
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;
}
其中returns这个错误:
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 的模板 class 实现,但我发现它是一个糟糕的解决方案,因为 Visual Studio 不会从此文件格式化任何内容。
试图定义 Utility.tpp:(错误的解决方案)
所以我在 Utility.h
中将 #include "Utility.cpp
替换为 #include "Utility.tpp
并定义...
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:(正确的解决方案,最终)
正如评论中让我注意到的那样,作为一个白痴,我在 .tpp
文件中导入了 Time
方法,显然我应该导入模板函数,所以在 Utility.tpp
包含 print_tuple
而 Utility.cpp
包含 3 个 Timer
函数。
从 Utility.h 中删除 #include "Utility.cpp" 行。
然后再次编译Utility.cpp
如果您要将 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++ 文件。如果您转到工具 -> 选项 -> 文本编辑器 -> 文件扩展名,您可以为格式添加文件扩展名。在扩展框中输入扩展名,select Microsoft Visual C++ 从编辑器下拉,然后单击添加。
现在您可以使用 tpp 文件而不是 cpp 文件,并且您不必记住从构建中排除 cpp 文件。您还可以遵循现代实践。
主要有两个问题:
您已将预期的 header 代码放置在“.cpp”文件中,默认情况下,您的 IDE 会将其视为主要源代码文件(翻译单元)。
您在 header 的全局命名空间中包含非 inline
函数定义(class 成员函数)。当 header 包含在两个或更多翻译单元中时,您将违反单一定义规则 (ODR)。实际上链接器会报错。
解决这个问题:
将文件扩展名从“.cpp”更改为例如“.hpp”或简单的“.h”。它是供 header 文件使用的代码。文件扩展名应该反映这一点,而不是误导。
声明函数 inline
,或将定义放在 class 定义中。
在其他新闻中,可能来自标准库的 <chrono>
时钟之一将满足您的目的,因此您不必在 header 中包含 <windows.h>
.它确实拖入了无数不合理的宏。包括,默认情况下,小写 min
和 max
宏,所以这段代码非常糟糕。
我有这个头文件:
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;
}
其中returns这个错误:
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 的模板 class 实现,但我发现它是一个糟糕的解决方案,因为 Visual Studio 不会从此文件格式化任何内容。
试图定义 Utility.tpp:(错误的解决方案)
所以我在 Utility.h
中将 #include "Utility.cpp
替换为 #include "Utility.tpp
并定义...
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:(正确的解决方案,最终)
正如评论中让我注意到的那样,作为一个白痴,我在 .tpp
文件中导入了 Time
方法,显然我应该导入模板函数,所以在 Utility.tpp
包含 print_tuple
而 Utility.cpp
包含 3 个 Timer
函数。
从 Utility.h 中删除 #include "Utility.cpp" 行。 然后再次编译Utility.cpp
如果您要将 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++ 文件。如果您转到工具 -> 选项 -> 文本编辑器 -> 文件扩展名,您可以为格式添加文件扩展名。在扩展框中输入扩展名,select Microsoft Visual C++ 从编辑器下拉,然后单击添加。
现在您可以使用 tpp 文件而不是 cpp 文件,并且您不必记住从构建中排除 cpp 文件。您还可以遵循现代实践。
主要有两个问题:
您已将预期的 header 代码放置在“.cpp”文件中,默认情况下,您的 IDE 会将其视为主要源代码文件(翻译单元)。
您在 header 的全局命名空间中包含非
inline
函数定义(class 成员函数)。当 header 包含在两个或更多翻译单元中时,您将违反单一定义规则 (ODR)。实际上链接器会报错。
解决这个问题:
将文件扩展名从“.cpp”更改为例如“.hpp”或简单的“.h”。它是供 header 文件使用的代码。文件扩展名应该反映这一点,而不是误导。
声明函数
inline
,或将定义放在 class 定义中。
在其他新闻中,可能来自标准库的 <chrono>
时钟之一将满足您的目的,因此您不必在 header 中包含 <windows.h>
.它确实拖入了无数不合理的宏。包括,默认情况下,小写 min
和 max
宏,所以这段代码非常糟糕。