Hi all. I'm starting to learn a bit about template functions and I have a question regarding declaration in a header file.

My code is as follows (from "Accelerated C++, chapter 8"):

#include "Split_Template_func.h"

#include <iostream>
#include <iterator>

using std::cout;        using std::cin;     using std::string;
using std::getline;     using std::ostream_iterator;

int main()
{
    string s;
    while (getline(cin, s))
        split(s, ostream_iterator<string>(cout, "\n"));
    return 0;
}

Split_Template_func.cpp:

#include "Split_Template_func.h"

#include <algorithm>
#include <iterator>
#include <cctype>

using std::string;      using std::ostream_iterator;        using std::find_if;
using std::isspace;

bool space(char c)
{
    return isspace(c);
}

bool not_space(char c)
{
    return !isspace(c);
}

template <class Out>
void split(const string& str, Out os)
{
    typedef string::const_iterator iter;

    iter i = str.begin();
    while (i != str.end())
    {
        //ignore leading blanks
        i = find_if(i, str.end(), not_space);

        // find end of next word
        iter j = find_if(i, str.end(), space);

        // copy the characters in [i,j)
        if (i != str.end())
            *os++ = string(i, j);

        i = j;
    }
}

Split_Template_func.h:

#ifndef GUARD_Split_Template_func_h
#define GUARD_Split_Template_func_h

#include <string>

bool space(char);
bool not_space(char);

template <class Out>
void split(const std::string&, Out);

#endif

I am getting an error on line 13 in main:
"undefined reference to 'void split<std::ostream_iterator<std::string, ..."

I assume it is because I have declared the template function incorrectly. How should I have done it?

Thanks.

Dani AI

Generated

The linker error occurs because the compiler must see the full definition of a function template at the point of use to generate an instantiation. You placed the template definition in a .cpp, but it is used from another translation unit (main). Two common fixes: make the template header-only, or keep the definition in the .cpp and explicitly instantiate the concrete types you need. For example, if you keep the definition in Split_Template_func.cpp, add:

// Split_Template_func.cpp
template void split(const std::string&, std::ostream_iterator<std::string>);

Optionally pair it with an extern template declaration in the header (C++11+) to suppress redundant instantiations:

// Split_Template_func.h
extern template void split(const std::string&, std::ostream_iterator<std::string>);

If you move the template definition into the header, ensure any helper functions it calls are also visible in every translation unit. Mark them inline (or put them in an anonymous namespace) to avoid multiple-definition errors, and be careful with std::isspace: it is only defined for unsigned char values or EOF. A safe header version is:

inline bool space(char c)     { return std::isspace(static_cast<unsigned char>(c)); }
inline bool not_space(char c) { return !space(c); }

More details: Templates: visibility and instantiation and std::isspace requirements.

Recommended Answers

All 3 Replies

Don't have much time but if I am not mistaken template functions must be in the header.

Try moving the the whole function from the CPP file to the H file.
(if you still want seperate files try renaming the CPP to TPP)

good luck

See this C++ FAQLite entry.

Long story short, the definition (implementation) of all templates must be visible in all translation units that use it. Best way to achieve that is to put it in the header file. You can still separate the declaration and definition (but still both in the header file), if you want a cleaner set of declarations.

Great. Sorted it. Thanks guys.

Be a part of the DaniWeb community

We're a friendly, industry-focused community of developers, IT pros, digital marketers, and technology enthusiasts meeting, networking, learning, and sharing knowledge.