function … has already a body & function template

2019-01-29 14:50发布

问题:

I have this header file:

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"

And this implementation file:

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;
}

Which returns this error:

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
...

The problem is not about guards (as suggested in this question) since I use #pragma once

I`ve read about of implementing .tpp files for template class implementation, but I find it an horrible solution, since Visual Studio will format nothing from this file.

trying to define Utility.tpp: (WRONG SOLUTION)

So I substituted #include "Utility.cpp with #include "Utility.tpp in Utility.h and define...

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;
}

And now the error returned is:

1>Memoization.obj : error LNK2005: "public: __cdecl Timer::Timer(void)" (??0Timer@@QEAA@XZ) already defined in HelloWorld.obj
...

This is the main btw:

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()
{
}

trying to define Utility.tpp: (RIGHT SOLUTION, FINALLY)

As in the comment made me notice, as an idiot I imported the Time methods in the .tpp file, and obviously I should have imported the templates function, and so in Utility.tpp contains print_tuple while Utility.cpp contains the 3 Timer functions.

回答1:

If you are going to include a cpp file into a header file then you need to exclude that cpp file from compiling in the project. If you do not then the header file will have what the cpp file has and when the compiler compiles the cpp file into an object you will have two copies of the of the definitions. One in the header file as it has a copy of the cpp file in it and one in the cpp file as that is what is actually compiled. This leads to the redefinition errors.

Once you remove Utility.cpp from being compiled then any other cpp file that includes Utility.h will have a copy the full source since Utility.cpp is also included.

To remove Utility.cpp from compiling see: How to exclude files from Visual Studio compile?


If the only reason you are using a cpp file is because tpp files do not format C++ code as C++ then you can just configure MSVS to treat tpp files as C++ files. If you go to Tools -> Options -> Text Editor -> File Extensions you can add a file extension to the formatting. Enter the extension in the extension box, select Microsoft Visual C++ from the editor drop down and then click add.

Now you can use a tpp file instead of a cpp file and you do not have to remember to exclude the cpp file from the build. You also get to conform to modern practices.



回答2:

There are main two problems:

  • You have placed intended header code in a ".cpp" file, which your IDE by default will treat as a main source code file (translation unit).

  • You are including non-inline function definitions (the class member functions) in the global namespace in a header. When that header is included in two or more translation units you will get a One Definition Rule (ODR) violation. In practice the linker will complain.

To fix this:

  • Change the filename extension from ".cpp" to e.g. ".hpp" or just plain ".h". It is code intended for header file use. The filename extension should reflect that, instead of misleading.

  • Declare the functions inline, or place definitions within the class definition.


In other news, possibly one of the <chrono> clocks, from the standard library, will serve your purpose so that you don't have to include <windows.h> in a header. It does drag in a zillion unreasonable macros. Including, by default, lowercase min and max macros, so this code is very ungood as given.



回答3:

Remove the #include "Utility.cpp" line from Utility.h. Then compile the Utility.cpp again