C++ template instantiation of function template parameters

13,054

Solution 1

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.

Solution 2

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
}

Solution 3

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.

Solution 4

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?

Share:
13,054
mavam
Author by

mavam

Post-doc at UC Berkeley. C++ CAF VAST Bro

Updated on June 18, 2022

Comments

  • mavam
    mavam almost 2 years

    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.

  • Martin York
    Martin York over 15 years
    Actually you can force an instantiation without using it. See below where I split code into three files and instantiate without using it.
  • SCFrench
    SCFrench over 15 years
    I stand by my statement. look at the "knarly typedef" in Martin York's post. There's no way you can replace myfunc with boost::function and get it to work.
  • David Rodríguez - dribeas
    David Rodríguez - dribeas almost 15 years
    ...following the edit: the result of boost::bind is an 'undefined' type, so explicit template instantiation in this case will not be a good solution (you can read bind.hpp implementation and determine the real type for the template instantiation, but then updates to the bind library can possibly break it as the type is not part of the interface).
  • David Rodríguez - dribeas
    David Rodríguez - dribeas almost 15 years
    It is a common mistake to assume that as in most cases templates are defined in the header file it must be so.
  • David Rodríguez - dribeas
    David Rodríguez - dribeas almost 15 years
    ... or when you explicitly instantiate it.
  • David Rodríguez - dribeas
    David Rodríguez - dribeas almost 15 years
    The problem I see with this solution is that the concrete type returned by boost::bind is not part of their public interface (docs). In the docs (boost::bind) it says that it is an 'unknown type', and to me that means that the concrete type (shown above) should not be used and that the type can be changed at any given time (breaking the code above).
  • David Rodríguez - dribeas
    David Rodríguez - dribeas almost 15 years
    @Head Geek: this answer is about the 'unknown type' returned by bind -which means a couple of things, first that it is not a boost::function<>, then that it can be changed at any time by the library implementor as it is not part of the public interface -they are not bound to return the same type in the next boost release.
  • Head Geek
    Head Geek almost 15 years
    If you plan to use them in more than one .cpp file, you must define them in a header file. I'm sure there are some labored exceptions to that rule, but it's a rule for a good reason.