C++ Error message redefinition of functions
Asked Answered
B

4

8

I am using two stacks to implement a queue class. My header file looks like:

#ifndef _MyQueue_h
#define _MyQueue_h
using namespace std;

template <typename T>
class MyQueue {

public:
    MyQueue();
    ~MyQueue();
    void enqueue(T element);
    T peek();
    void dequeue();
    int size();
    bool empty();

private:
    int count;
    stack<T> stk1;
    stack<T> stk2;
};
# include "MyQueue.cpp"
# endif

And my cpp (implementation) file looks like:

#include <stack>
#include "MyQueue.h"
using namespace std;

template <typename T>
MyQueue<T>::MyQueue()
{
    count = 0;
}

template <typename T>
MyQueue<T>::~ MyQueue()
{
}

template <typename T>
void MyQueue<T>::enqueue(T element)
{
    stk1.push(element);
    count ++;
}

(other functions omitted).

However, using Xcode 4.5, it keeps saying that my functions (MyQueue, ~MyQueue, enqueue, peek, etc.) are redefined. Can anyone help me to clarify where have I redefined them?

Thank you

Blueweed answered 13/11, 2013 at 22:2 Comment(1)
You should never include a source file (i.e files ending in .cpp, .cc, or .C). Also, make sure to include the header file in which your stack is defined, unless you are using the STL stack.Eudemon
R
5

You're trying something which I really don't like. It's a pretence.

Remove #include "MyQueue.cpp", replace it with the content of MyQueue.cpp, delete the file MyQueue.cpp. Now everything will work.

You are trying to pretend the template code can be split into header file and implementation file. But because it can't you have to cheat by including the implementation file in the header file. It's less confusing if you don't cheat or pretend and just have one file, the header file, with everything in it.

The precise reason that you get a redefinition is that you are compiling your cpp file, which includes your header file, which includes your cpp file again. So the content of the cpp file gets compiled twice.

Rianon answered 13/11, 2013 at 22:7 Comment(4)
I've seen somebody recently use a .tpp file to store the implementation and have it included at the end of the .hpp file. Would you approve of that? It means nothing to the compiler of course, but it might seem 'tidier' to some people.Goldoni
I've seen that too, it seems pointless to me. What's the point of having a file which is referenced in only one place when it is included in another file. Again why not just have a single file? But these are style issues, it can be made to work that way, just as long as you don't try to compile the tpp file.Rianon
Thanks. It is just a practice :) I deleted "#include "MyQueue.h" in the cpp file, but now it still does not compile. Xcode complains that (at the first function it encounters, the constructor, MyQueue<T>::MyQueue()): 1) "Unknown type name 'MyQueue'," and 2), "Expected unqualified_id." Any thoughts why this is so?Blueweed
@HaydenCoyle Read my answer again, you did something completely different from what I suggested. Basically you should delete the cpp file and put all the code in the header file.Rianon
B
3

In C and C++, #include behaves like a copy and paste. Everytime you see

#include "file" 

it should be treated as if you literally retyped the whole file in that one spot. So if you compile MyQueue.cpp, the preprocessor will prepend the contents of MyQueue.h, which itself tacks on a duplicate of MyQueue.cpp evidenced by

#include "MyQueue.cpp" 

and then follows the native content of MyQueue.cpp.

So the result of

#include "MyQueue.cpp"

inside MyQueue.h, is the same as if you had written one big file with the contents of MyQueue.h, MyQueue.cpp and MyQueue.cpp again. (with the include of stack in there as well of course) That is why the compiler complained about functions getting redefined.

The Duplicate inserted from the

#include "MyQueue.cpp" 

might also contain the line

#include "MyQueue.h"

but I think the include guards (ifndef,endif) protected against a recursive expansion since that did not seem to be an issue.

I would like to point out that putting all the implementation code and declaration code in the same file for templates is not the only solution, as others suggest.

You just have to remember that templates are generated at compile time and include them wherever they are needed. Like Aaron has pointed out, you can even force generate a template for a specific type or function so it's accessible to all units.

In this way, the definition of a function can be embedded in an arbitrary module and the rest of the modules won't complain that a function isn't defined.

I like to declare small templates and template interfaces in header files and put large implementations in special files that are just glorified headers. You could put some special extension like .tpp .cppt or whatever to remind yourself that it is code you have to include somewhere (which is what I do).

It is a suitable alternative to storing large implementations in header files that must be pasted around just to refer to the type (or function signature). And it works absolutely fine, for years now.

So for example, when I am ready to compile my big program, I might have a file called structures.cpp that I designate to implement lots of small structures I use, as well as instantiate all the templates for my project.

all the other .cpp files in the project need to include "mylib/template_structs.h" in order to create instances of templates and call functions with them. whereas structures.cpp only needs to include "mylib/template_structs.cppt" which in turn may include template_structs.h or else structures.cpp would have to include that as well first.

If structures.cpp calls all the functions that any other .cpp files would call for that template then we are done, if not, then you'd need the extra step of something like

template class mynamespace::queue<int> ;

to generate all the other definitions the rest of the project's modules would need.

Baalbeer answered 24/4, 2018 at 1:4 Comment(6)
Stack Overflow is a Q & A site. The answer box is only for complete answers to the question. Maybe I am just not seeing it, but where in your post are you answering where the functions are redefined? If you are not, I suggest you answer the question at the top and then add the remainder of your post as a sidebar (prefix each line with >). This will help to prevent the answer from being deleted during review for not answering the question that was asked.Milliken
I was under the impression that on SO, answers that solve the underlying problem the asker has count as answers. I added my answer because I was afraid people would come to this page taking away the only remedy was to include all the code in one file and I saw no one introduce this exact approach. But since I am still building my understand of SO, I will trust your judgment. I don't mind deleting the answer unless you think it would be more useful to leave it as a sidebar.Baalbeer
True, answers that solve the underlying problem are fine. However, if that is the case you didn't make it very clear (the fact that it wasn't clear is why I commented instead of flagged your answer). The best approach is to put the question that was asked at the top of the answer (prefixed by >) and then to immediately address it. No need to make the post a sidebar. In this case you could say something like "you don't necessarily have to do that.." Make it easy for reviewers to understand you are addressing the OP directly (even though you are also referring to other answers).Milliken
I can do that, I'll try to edit it now and if you would be so kind please give me feedback on if it looks better.Baalbeer
BTW - on SO posts are ordered by upvotes which may change over time, so using the word "above" to refer to another answer is not appropriate. Instead, you should link to the other answer by using the share link below it.Milliken
I see, would you say that even for posts on the same page a link is preferred to only referencing user name ? I suppose on a page with lots of answers that would be ideal. I sidebar'd the original content but I'm not exactly sure if I did what you meant in your suggestion.Baalbeer
G
1

The problem is that, when compiling the cpp file, the cpp file includes the .h file and then the .h file includes the .cpp file. Then you have two copies of the cpp code in the same 'translation unit' at the same time.

But there are a few different solutions to this, it depends what your ultimate goal is.

  1. The simplest, and most flexible solution is simply to remove all the template stuff from the .cpp file and put it into the .h file instead. You might think this is bad design, you've probably been taught to keep declarations and definitions in separate files, but this is how templates are usually implemented. (Welcome to the weird and wonderful world of C++ templates!)

  2. But, perhaps these are to be 'private' templates, only to be used from one .cpp file. In that case, the best thing to do is simply to move everything from the .h file into the .cpp file.

  3. There is a third approach, which doesn't get enough attention in my opinion. First, remove the #include "MyQueue.cpp" from your .h file, and recompile. It's quite possible that will just work for you. However, if your project has multiple .cpp files, you might get linker errors about undefined reference to MyQueue<string> :: MyQueue(). (where string is replaced with whatever you are putting in your queue. These linker errors can be fixed by placing template MyQueue<string>; at the end of the file that has the definitions of the templates (your MyQueue.cpp). This means you have to do this once for each type that you plan to store in your queue, but you might see this as an advantage as it helps you remember which types are supported by your queue.

Goldoni answered 13/11, 2013 at 22:44 Comment(0)
F
0

when you include something it replaces the included file with the code within so when you call #include "MyQueue.cpp" it replaces that with the cpp file, then your cpp file redefines it. Getting rid of the line will fix it.

Frenchpolish answered 13/11, 2013 at 22:12 Comment(2)
Thanks. I deleted "#include "MyQueue.h" in the cpp file, but now it still does not compile. Xcode complains that (at the first function it encounters, the constructor, MyQueue<T>::MyQueue()): 1) "Unknown type name 'MyQueue'," and 2), "Expected unqualified_id." Any thoughts why this is so?Blueweed
@HaydenCoyle, this answer wasn't very clear about which line to delete. You can possibly just delete # include "MyQueue.cpp" from the header file. If you are determined to keep these two files separately, then you should reinsert the #include "MyQueue.h in the cpp file.Goldoni

© 2022 - 2024 — McMap. All rights reserved.