4

other.cpp

#include <iostream>
#include <string>
using std::cout;

template <typename T>
std::ostream & cprint(T &t,  std::string msg = "Container", std::ostream & stream = cout){
    stream << msg << "\t{ ";
    for(typename T::iterator it = t.begin(); it != t.end(); it++)
        stream << *it << " ";
    stream << "}\n";

    return stream;
}

I have a template function in a separate file. and i am trying to forward declare it in main.cpp file.

extern template std::ostream & cprint<std::vector<int>>
(T &t,  std::string msg = "Container", std::ostream & stream = cout);

I have tried something as above but it didn't work for me.

Edit: assume other.cpp has a basic function as below,

template <typename T>
void func(T x){
    cout << x << endl;
}

how to instantiate this function in main.cpp?

2 Answers2

4

Keep in mind that what you're asking about is (usually) NOT the right way to work with templates.

What you should do is to declare your template in a header and get rid of the extern declarations and explicit specializations.

Putting templates into .cpps usually isn't very convenient because they require you to specify all types you want them to work for in the same .cpp.

But, on the other hand, it improves compilation speed and there is a small chance that the resulting binary will be smaller.


How to fix your code:

I made some changes to your code to make it work.

I also made some minor improvements. Ask in comments if you find them confusing.

// 1.cpp
#include <iostream>
#include <string>
#include <vector>

template <typename T>
std::ostream &cprint(const T &t, std::string msg = "Container", std::ostream &stream = std::cout)
{
    stream << msg << " { ";
    for(typename T::const_iterator it = t.cbegin(); it != t.cend(); it++)
        stream << *it << " ";
    stream << "}\n";
    return stream;
}

template std::ostream &cprint(const std::vector<int> &, std::string, std::ostream &);

// 2.cpp
#include <string>
#include <iostream>
#include <vector>

template <typename T> std::ostream &cprint(const T &, std::string = "Container", std::ostream & = std::cout);

int main()
{
    std::vector<int> v{1,2,3};
    cprint(v);
}

Important parts are following:

  • Explicit instantination. It must be located in a same file as a template definion.
    template std::ostream &cprint(const std::vector<int> &, std::string, std::ostream &);

  • Declaration. It must be in a file where you want to use your template. template <typename T> std::ostream &cprint(const T &, std::string = "Container", std::ostream & = std::cout);

Note that you can manually specify template arguments in explicit instantination and extern declaration if compiler can't deduce them.


Doing the same thing with a dummy template which was edited into a question is left as an excercise to the reader.

Community
  • 1
  • 1
HolyBlackCat
  • 78,603
  • 9
  • 131
  • 207
  • You are right. I was totally doing it in a wrong way. i should have used an header and define everything in header. – Rıfat Tolga Kiran Jan 15 '17 at 23:32
  • The answer solves the problem, but I don't agree that template definitions only belong in headers or that their primary purpose is to decrease compilation time or binary size; in fact they can do the opposite, especially if they're defined in headers. – gcbenison Aug 13 '17 at 14:42
  • @gcbenison Point taken, edited the answer. I agree, sometimes putting templates into `.cpp` could be useful. The part about bettter compilation speed and small binary size was about templates located in `.cpp`s, *not* headers. I changed the wording to make it more clear. – HolyBlackCat Aug 13 '17 at 17:42
0

Apart from this, You should also read: using extern template (C++11)

To defer instantiation using extern, you must match the function signature exactly, so below will not work unless T is defined as std::vector<int> or its alias - which matches your template declaration.:

extern template std::ostream & cprint<std::vector<int>>
(T &t,  std::string msg = "Container", std::ostream & stream = cout);

To fix,

extern template std::ostream& cprint<std::vector<int>>
(std::vector<int>&, std::string msg = "Container", std::ostream & stream = cout);

See it Here

More concretely,

In Header.hpp file, I have its declaration and its deferment:

template <typename T>
std::ostream & cprint(T &t,  std::string msg = "Container", std::ostream & stream = cout){
    stream << msg << "\t{ ";
    for(typename T::iterator it = t.begin(); it != t.end(); it++)
        stream << *it << " ";
    stream << "}\n";

    return stream;
}

//defer instantiation
extern template std::ostream& cprint<std::vector<int>>
(std::vector<int>&, std::string msg = "Container", std::ostream & stream = cout);

Now, in main.cpp file, I have:

int main(){
    std::vector<int> v{1, 2, 3, 4, 5};
    cprint(v);
}

In the Header.cpp file, I have its instantiation:

template std::ostream& cprint<std::vector<int>>
(std::vector<int>&, std::string msg = "Container", std::ostream & stream = cout);

As seen Here

=====================

EDIT: With respect to your recent edit:

Edit: assume other.cpp has a basic function as below,

template <typename T>
void func(T x){
    cout << x << endl;
}

how to instantiate this function in main.cpp?

If the translation unit, main.cpp sees the declaration of the function template func(either from an included header, or you (re-(forward-))declared it in main.cpp, it will compile as calling a function template-specialization1, but the linker will be unable to find the function template-specialization unless it sees the definition of that function template-specialization as instantiated in (another) translation unit.


Note: "function template specialization" is a function instantiated from a function template. See temp.fct/1

Community
  • 1
  • 1
WhiZTiM
  • 21,207
  • 4
  • 43
  • 68