C++: template function instantiation does not work with string as return value. Why?
Hi,
I'm new with templates in C++, but there is something that's really bugging me: I can make references of a template function from one .cpp file in a .h file and use that function in another .cpp file if I instantiate the template with the appropriate types. But when the return type is std::string, linker errors occur. EXAMPLE CODE: main.cpp Code:
#include "template.h" Code:
#include <iostream> template.h Code:
#include <string> Code:
/tmp/ccgjbq5N.o: In function `main': Is there a (practicable) solution for this or do I just have to move templates that won't work like that into header files? |
Well, I am no C++ expert, but I see 'main.cpp' includes 'template.h', and I do not see that 'template.h' includes 'template.cpp'. Nor 'main.cpp' includes 'template.cpp'.
So, if my observation is correct, why do you have 'template.cpp' in the first place ? |
Quote:
This is the usual way to split up code in more than one .cpp file, .h files [=headers] are interfaces between the .cpp files. Here it even works with the template funtions (which are indeed usually just dumped into header files), just the function that returns a string doesn't work that way. As I stated in my first post, everything works as long as I don't use a template function that returns a string. I'm afraid your reply misses the point (if you have further ideas, you are still invited to post them here). One thing I can think of is, the string type seems to be a template itself (the other types aren't), which complicates things. But then I can't find out what the problem is. Maybe that one really is too complicated for me, but I'd rather want to know than to guess. |
The failure to find test<std::string>() is easy to understand.
The definition of test is in a separate compilation unit, so you needed to explicitly instantiate every instance of that which you might use. That is what your code is doing with: Code:
template void test <int> (int); Similarly the failure to find the instantiation of str() needed for str("test"): The compiler has deduced char const* and you simply made the error of providing char* instead. Edit: The momentary brain lapse below was in my original response. But then I realized I had const&T where you have T, which is why the compiler can't misunderstand "test" for you but did for me: Similarly I understand the failure to find the instantiation of str() needed for str("test"). But I don't understand why that almost worked and could be fixed easily. In many places in my own code I have written the equivalent of template <class T> std::string str(T); str("test") and been frustrated that the compiler instantiated something like str<const char[5]> when I really really wanted str<char const*> So I assumed (without knowing where to check in the C++ standard) that template deduction on "test" produces const char[5] instead of char const*. That has made a lot of my code harder to get right. But your error messages show a much better behavior by the compiler. It has deduced char const* and you simply made the error of providing char* instead. |
As the compiler is telling you, you don't haven't instantiated what you need, and the compiler can't do it itself because it can't see the implementations of those templates. Each cpp is a processed by itself, nothing from any other cpp is considered ("Translation Unit").
Just save yourself a lot of trouble and put the implementations into the header file, or include from the header file a "template implementation" file. |
Quote:
|
Quote:
So the information in template.h is known to the compiler during each compile (because it is #included in each). But the information in template.cpp is not know while compiling main.cpp and is usable by main.cpp only through linking (which in g++ is able to connect to already instantiated instances of a template from other compilation units, but is not able to instantiate new instances). |
Quote:
Quote:
|
Quote:
I don't believe you would get the same result. Even fixing just one of the two bugs should have changed the error messages. Repost your code with your understanding of those two corrections and with the error messages if any from recompiling. |
Quote:
So what was the other bug? I read your posts, but I don't see anything besides the "char const*" issue. |
Quote:
|
Quote:
Here is a working version (without string as return parameter), so you get the idea: main.cpp Code:
#include "template.h" Code:
#include <iostream> Code:
#include <string> Compile this with the command "g++ main.cpp template.cpp" or just "g++ *.cpp". Any ideas appreciated I gtg soon, will check on later responses when I'm back at my PC again ;) |
your template should be in the header file.
template.cpp should be template.hpp. template.cpp is not needed. then it compiles. |
Quote:
I know that much. That would be common practice. I'm just wondering why the instantiation method does work with simple types and does not work with the string type (note that I also posted a fully working example). Or rather, I would like to know if it's possible to instantiate template functions that return a string and use them from different .cpp files (or object files or translation units, you get the idea). Again, this does work in the second example I posted, but not when the return type is string. |
Quote:
I also spotted another mistake: the "test" function was not instantiated with the string type. After I added that instantiation, everything worked. johnsfine already explained that in his first post, and I only understood that now. Sorry for that. So everything was just a few mistakes on my part. Thank you for your contributions. The following code works completely, I'm just posting it for convenience: main.cpp Code:
#include "template.h" Code:
#include <iostream> Code:
#include <string> |
Just for information. You don't need to do it that way. With templates (in gcc) you can include the code in the header file and then the compiler will work out which templated functions will be required. So the following will give the same results:
template.h Code:
#include <string> Code:
#include "template.h" |
Quote:
TITiAN clearly wanted to do that and knew the basics of how to do that and merely made a couple minor errors in the details. But then started thinking there was something more fundamental wrong with the mechanism, rather than spotting the couple details that were wrong. |
One thing that confused me for most of the time was that g++ calls the string type this:
Code:
std::basic_string<char, std::char_traits<char>, std::allocator<char> > Right now I'm having issues with a template function that accepts "T const&" as an argument, but before I start another thread, I'll tear out my own hair ;) |
Quote:
|
|
Quote:
Whose question do you think you're answering? What part of that question do you think you're answering? What part of that long document do you think provides that answer? The OP in this thread was never confused about the basic questions of why it is easier to have the definition in header rather than cpp files, nor the more advanced question of what you need to do when you really want the definition in a cpp anyway. The OP was only messing up some details within that and already solved even those details. The link you posted seems to only cover the basics of that, which the OP didn't need and which others in this thread should already understand from what was already in this thread. The OP (and maybe me as well) are now confused about some of the strange consequences of using T const& as the parameter type in a templated function. Is there something I missed in that document you linked that helps with that question? |
well excuse me for not meeting your criteria.
I thought it may help the OP. It is also a useful resource for C++ problems. I thought it was fairly relevent myself, the FAQ says: Quote:
if you read the FAQ it refers to the next two which give solutions to the problem. OP: Quote:
Quote:
Quote:
obviously I am an idiot. |
Quote:
|
Quote:
I have complicated examples, some of which simplify to the example below and others I'm not sure. When I started being confused by this topic 8 years ago, I would not have understood the behavior of the complicated examples that do simplify to the one below. Once it is that simplified as shown below, the behavior is much easier to understand. But I expect many experienced C++ programmers would be surprised at the output of even this simple program: Code:
#include <iostream> Code:
size = 5 In more complicated cases, I used to make the simple assumption that "test" was a char const* which is 4 bytes (in x86) so that a reference to "text" would be a reference to a char const* which would be a reference to a type that is 4 bytes, just a 1.3 is a double, which is 8 bytes while a reference to 1.3 is a reference to a type that is 8 bytes. In other words, I thought passing by reference gives you a reference to the type that would have been passed if the same parameter had been passed by value. But that is not generally true. When writing a very general template that must be prepared to deal with many kinds of input, some of which really ought to be passed by const&, it may be tricky to deal with this extra difference between passing by value and passing by const& (that it is a const& to some other type than would have been passed by value). |
You'll get the same difference without templates, it's because one is measuring the size of the literal, the other measuring the size of the pointer:
Code:
#include <iostream> Code:
4 |
Quote:
|
I still don't understand why I can't use "const char* const&" as an argument:
main.cpp Code:
#include "template.h" Code:
#include <iostream> Code:
#include <string> Code:
/tmp/ccS1eD8a.o: In function `main': Code:
template void test<char[5]> (char const(&)[5]); BTW this works: Code:
#include <iostream> |
I figured something out: If I change this line from main.cpp
Code:
test("test); Code:
test((const char*)"test"); So I guess the compiler can automatically convert char[5] into const char*, but not when it has to deduce that char[5] is const char* as in const char* const&. |
Dear Diary,
Today was a lucky day: I found a solution for the problem the use of the "T const&" template type has. The working version looks like this: main.cpp Code:
#include "template.h" Code:
#include <iostream> Code:
#include <string> The function Code:
void test(char const* t) {test<char const*>(t);} |
I wonder if there has been some confusion about how references work?
As I understand it the reference is established in the declaration and not in the calling of a function. So if you pass a reference of an object to a function that is not expecting a reference then a copy is made. Whereas if in the declaration the function says that it expects a reference then a reference will be used. The following example first looks at it from a function perspective and then a templated function. template.h Code:
#include <string> Code:
#include "template.h" Code:
#include "template.h" Code:
In constructor. My recommendation would be to have the concepts clear by writing a non-templated function if the creation of the templated version is becoming problematic |
All times are GMT -5. The time now is 09:01 PM. |