Question

I have the following problem using template instantiation [*].

file foo.h

class Foo
{
public:
    template <typename F>
    void func(F f)

private:
    int member_;
};

file foo.cc

template <typename F>
Foo::func(F f)
{
     f(member_);
}

file caller.cc

Foo::func(boost::bind(&Bar::bar_func, bar_instance, _1));

While this compiles fine, the linker complains about an undefined symbol:

void Foo::func<boost::_bi::bind_t...>

How can I instantiate the function Foo::func? Since it takes a function as argument, I am little bit confused. I tried to add an instantiation function in foo.cc, as I am used to with regular non-function types:

instantiate()
{
    template<> void Foo::func<boost::function<void(int)> >(boost::function<void(int)>);
}

Obviously, this does not work. I would appreciate if someone can point me in the right direction.

Thanks!

[*] Yes, I read the parashift FAQ lite.

Was it helpful?

Solution

The answer to this is compiler dependent. Some versions of the Sun C++ compiler would handle this automatically by building a cache of template function implementations that would be shared across separate translation units.

If you're using Visual C++, and any other compiler that can't do this, you may as well put the function definition in the header.

Don't worry about duplicate definitions if the header is included by multiple .cc files. The compiler marks template-generated methods with a special attribute so the linker knows to throw away duplicates instead of complaining. This is one reason why C++ has the "one definition rule".

Edit: The above comments apply in the general case where your template must be capable of linking given any type parameters. If you know a closed set of types that clients will use, you can ensure they are available by using explicit instantiation in the template's implementation file, which will cause the compiler to generate definitions for other files to link against. But in the general case where your template needs to work with types possibly only known to the client, then there is little point in separating the template into a header file and and implementation file; any client needs to include both parts anyway. If you want to isolate clients from complex dependencies, hide those dependencies behind non-templated functions and then call into them from the template code.

OTHER TIPS

Splitting it into files Like you want:
Not that I recommend this. Just showing that it is possible.

plop.h

#include <iostream>
class Foo
{
public:
    Foo(): member_(15){}


    // Note No definition of this in a header file.
    // It is defined in plop.cpp and a single instantiation forced
    // Without actually using it.
    template <typename F>
    void func(F f);

private:
    int member_;
};


struct Bar
{
     void bar_func(int val) { std::cout << val << "\n"; }
};

struct Tar
{
    void tar_func(int val) { std::cout << "This should not print because of specialisation of func\n";}
};

Plop.cpp

#include "plop.h"
#include <boost/bind.hpp>
#include <iostream>

template <typename F>
void Foo::func(F f)
{
     f(member_);
}

// Gnarly typedef
typedef boost::_bi::bind_t<void, boost::_mfi::mf1<void, Bar, int>, boost::_bi::list2<boost::_bi::value<Bar>, boost::arg<1> (*)()> > myFunc;

// Force the compiler to generate an instantiation of Foo::func()
template void Foo::func<myFunc>(myFunc f);

// Note this is not a specialization as that requires the <> after template.
// See main.cpp for an example of specialization.

main.cpp

#include "plop.h"
#include <boost/bind.hpp>
#include <iostream>

// Gnarly typedef
typedef boost::_bi::bind_t<void, boost::_mfi::mf1<void, Tar, int>, boost::_bi::list2<boost::_bi::value<Tar>, boost::arg<1> (*)()> > myTar;

// Specialization of Foo::func()
template<> void Foo::func<myTar>(myTar f)
{
    std::cout << "Special\n";
}
// Note. This is not instantiated unless it is used.
// But because it is used in main() we get a version.

int main(int argc,char* argv[])
{
    Foo f;
    Bar b;
    Tar t;

    f.func(boost::bind(&Bar::bar_func, b, _1)); // Uses instantiation from plop.cpp
    f.func(boost::bind(&Tar::tar_func, t, _1)); // Uses local specialization
}

Are you including foo.cc into caller.cc. Instantiation is something that happens at compile time -- when the compiler sees the call in caller it makes instantiated versions of the templates but needs to have the full definition available.

I think what they're both referring to is that template function definitions (not just declarations) have to be included in the file where they're used. Template functions don't actually exist unless/until they're used; if you put them in a separate cc file, then the compiler doesn't know about them in the other cc files, unless you explicitly #include that cc file into either the header file or the file that's calling them, due to the way the parser works.

(That's why template function definitions are generally kept in the header files, as Earwicker described.)

Any clearer?

I believe Earwicker is correct. The problem with explicitly instantiating the template member function func in this case is that the type returned by boost::bind is implementation dependent. It is not a boost::function. A boost::function can contain a boost:bind because it has a template assignment operator that deduces the type of the right-hand side (the boost::bind result). In this particular use of func in caller.cc, with this particular implementation of boost, the type of the boost::bind is actually they type mentioned in the linker error between the < and > (ie. boost::_bi::bind_t...). But explicitly instantiating func for that type probably will have portability problems.

Licensed under: CC-BY-SA with attribution
Not affiliated with StackOverflow
scroll top