templates??

  • Thread starter Rene Ivon Shamberger
  • Start date

R

Rene Ivon Shamberger

Oops, I posted this question before, but in the wrong place :-o
I have double checked this method, and it is fine. However, there is a problem at linker time. Perhaps one of you can do me the honours to check out and let me know what I am doing wrong? Thanks!!

---- myclass.hpp -----
#ifndef ABC_MYCLASS_HPP
#define ABC_MYCLASS_HPP

#include <string>

namespace ABC{
class myClass{
private:
std::string str;
public:
MyClass(){str.clear();}
virtual ~MyClass(){}
template< typename T> std::string& format( const T& data );

};//class
}// name space
#endif

---- myclass.cpp ----
#ifndef ABC_MYCLASS_HPP
#include "myclass.hpp"
#endif
template< typename T>
std::string& ABC::MyClass::format(const T& data){
std::stringstream num;
num.flush();
num << data;
str = num.str();
return str;
}

---- main.cpp -----
#include <iostream>
#include "myclass.hpp"
int main(){
ABC::MyClass mc;
int i = 100;
std::string text = mc.format(i); //<<-- linker error
....

return 0;
}


1>------ Build started: Project: ABC, Configuration: Release Win32 ------
1> main.cpp
1>main.obj : error LNK2001: unresolved external symbol "public: class std::basic_string<char,struct std::char_traits<char>,class std::allocator<char> > & __thiscall ABC::MyClass::format<int>(int const &)" ([email protected]@[email protected]@@[email protected][email protected]@[email protected]@[email protected]@[email protected]@[email protected]@[email protected])
1>{myfolder}\Release\ABC.exe : fatal error LNK1120: 1 unresolved externals
========== Build: 0 succeeded, 1 failed, 0 up-to-date, 0 skipped ==========
 
Ad

Advertisements

V

Victor Bazarov

Oops, I posted this question before, but in the wrong place :-o I
have double checked this method, and it is fine. However, there is a
problem at linker time. Perhaps one of you can do me the honours to
check out and let me know what I am doing wrong? Thanks!!
[..]

See FAQ 35.16. Start here: http://www.parashift.com/c++-faq/

And always, *ALWAYS* RTFFAQ list before posting.

V
 
Ad

Advertisements

R

Rui Maciel

Rene said:
Oops, I posted this question before, but in the wrong place :-o
I have double checked this method, and it is fine. However, there is a
problem at linker time. Perhaps one of you can do me the honours to check
out and let me know what I am doing wrong? Thanks!!

Your example has multiple issues, some of which I suspect were only
introduced when you wrote your usenet post.

Nevertheless, the main issue which I suspect you are experiencing is that
you've separated the declaration and definition of your template member
function.

The main issue is that when you use templates, you are instructing the
compiler to generate code on demand, and the compiler can only generate code
based on the information that you passed to it. This information is the
translation unit, which is essentially the source file that you instructed
the compiler to compile, with all the calls to #include replaced with the
contents of the header files. So, for example, when you try to compile
myclass.cpp, the compiler replaces #include "myclass.hpp" with the contents
of myclass.hpp. When you try to compile main.cpp, it includes the contents
of iostream and myclass.hpp.

Now, let's look into what happen when if you compile your myclass.cpp and
main.cpp.

If you compile myclass.cpp, as said previously, the translation unit
consists of the contents of myclass.cpp with the #include "myclass.hpp"
replaced with the contents of myclass.hpp. Therefore, essentially, the
translation unit will be equivalent to the following code:

<code>
#include <string> // not replaced, to cut to the chase

namespace ABC{
class myClass{
private:
std::string str;
public:
MyClass(){str.clear();}
virtual ~MyClass(){}
template< typename T> std::string& format( const T& data );

};

template< typename T>
std::string& ABC::MyClass::format(const T& data){
std::stringstream num;
num.flush();
num << data;
str = num.str();
return str;
}
</code>


Notice that, in that code, although you've defined a template member
function, you haven't instantiated it. Therefore, when compiling that code,
the compiler won't generate any MyClass::format().

With regard to main.cpp, the translation unit will be equivalent to the
following code:

<code>
#include <iostream> // included in main.cpp, not replaced here
#include <string> // included in myclass.hpp, not replaced here

namespace ABC{
class myClass{
private:
std::string str;
public:
MyClass(){str.clear();}
virtual ~MyClass(){}
template< typename T> std::string& format( const T& data );

};
}

int main(){
ABC::MyClass mc;
int i = 100;
std::string text = mc.format(i);

return 0;
}
</code>

Notice that in the above code format() has been declared, but no definition
was given. You haven't supplied the compiler with enough information to
generate the code for any version of that member function. Therefore, the
compiler generates nothing. But then, in main(), you've added a call to
MyClass::format<int>(), which instructs the compiler to call that function,
a function that doesn't exist because it was impossible to generate due to
the absence of a function definition.

To fix this issue, you have at least* two options:
- make sure that every translation unit will include a complete definition
of MyClass::format<typename T>(), and that your code will define it only
once in the entire project.
- instantiate every instance of MyClass::format<typename T>() which you will
ever use in your programs


The first one consists essentially in making sure that when myclass.hpp is
included, it also contains the definition for the template member function.
You get this by including the definition in your myclass.hpp file, either
directly (i.e., paste the contents of myclass.cpp into myclass.hpp) or
indirectly (i.e., place a #include "myclass.cpp" right after your definition
of MyClass in your myclass.hpp).

The second one is to simply force the compiler to instantiate every version
of MyClass::format() that you will use throughout your code. In your
example, this would be done by adding the following line to your
myclass.cpp, after the template member function definition:

template std::string& ABC::MyClass::format<int>(const int& data);


Essentially, that's all there is to know.


Hope this helps,
Rui Maciel

* with C++11 there is a third way, which is to declare a template member
function as extern. Some compilers don't support it yet, though.
 

Ask a Question

Want to reply to this thread or ask your own question?

You'll need to choose a username for the site, which only take a couple of moments. After that, you can post your question and our members will help you out.

Ask a Question

Top