templates??

Discussion in 'C++' started by Rene Ivon Shamberger, Dec 5, 2012.

  1. 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 &)" (??$format@H@MyClass@ABC@@QAEAAV?$basic_string@DU?$char_traits@D@std@@V?$allocator@D@2@@std@@ABH@Z)
    1>{myfolder}\Release\ABC.exe : fatal error LNK1120: 1 unresolved externals
    ========== Build: 0 succeeded, 1 failed, 0 up-to-date, 0 skipped ==========
     
    Rene Ivon Shamberger, Dec 5, 2012
    #1
    1. Advertising

  2. On 12/5/2012 9:53 AM, Rene Ivon Shamberger wrote:
    > 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
    --
    I do not respond to top-posted replies, please don't ask
     
    Victor Bazarov, Dec 5, 2012
    #2
    1. Advertising

  3. Rene Ivon Shamberger

    Rui Maciel Guest

    Rene Ivon Shamberger wrote:

    > 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.
     
    Rui Maciel, Dec 5, 2012
    #3
    1. Advertising

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

It takes just 2 minutes to sign up (and it's free!). Just click the sign up button to choose a username and then you can ask your own questions on the forum.
Similar Threads
  1. Fred
    Replies:
    1
    Views:
    635
    Neredbojias
    Sep 26, 2005
  2. John Harrison

    using templates in templates

    John Harrison, Jul 31, 2003, in forum: C++
    Replies:
    8
    Views:
    404
    Torsten Curdt
    Jul 31, 2003
  3. JKop
    Replies:
    3
    Views:
    523
  4. Tom McCallum

    Templates within templates

    Tom McCallum, Aug 4, 2004, in forum: C++
    Replies:
    2
    Views:
    381
    tom_usenet
    Aug 4, 2004
  5. recover
    Replies:
    2
    Views:
    859
    recover
    Jul 25, 2006
Loading...

Share This Page