Why Can't Templates Be Declared in a Function

Why can't templates be declared in a function?

The problem is probably linked to the historical way templates were implemented: early implementation techniques (and some still used today) require all symbols in a template to have external linkage. (Instantiation is done by generating the equivalent code in a separate file.) And names defined inside a function never have linkage, and cannot be referred to outside of the scope in which they were defined.

Why can templates only be implemented in the header file?

Caveat: It is not necessary to put the implementation in the header file, see the alternative solution at the end of this answer.

Anyway, the reason your code is failing is that, when instantiating a template, the compiler creates a new class with the given template argument. For example:

template<typename T>
struct Foo
{
T bar;
void doSomething(T param) {/* do stuff using T */}
};

// somewhere in a .cpp
Foo<int> f;

When reading this line, the compiler will create a new class (let's call it FooInt), which is equivalent to the following:

struct FooInt
{
int bar;
void doSomething(int param) {/* do stuff using int */}
}

Consequently, the compiler needs to have access to the implementation of the methods, to instantiate them with the template argument (in this case int). If these implementations were not in the header, they wouldn't be accessible, and therefore the compiler wouldn't be able to instantiate the template.

A common solution to this is to write the template declaration in a header file, then implement the class in an implementation file (for example .tpp), and include this implementation file at the end of the header.

Foo.h

template <typename T>
struct Foo
{
void doSomething(T param);
};

#include "Foo.tpp"

Foo.tpp

template <typename T>
void Foo<T>::doSomething(T param)
{
//implementation
}

This way, implementation is still separated from declaration, but is accessible to the compiler.

Alternative solution

Another solution is to keep the implementation separated, and explicitly instantiate all the template instances you'll need:

Foo.h

// no implementation
template <typename T> struct Foo { ... };

Foo.cpp

// implementation of Foo's methods

// explicit instantiations
template class Foo<int>;
template class Foo<float>;
// You will only be able to use Foo with int or float

If my explanation isn't clear enough, you can have a look at the C++ Super-FAQ on this subject.

Why can't template functions be passed as a template template parameter?

I don't know for sure the answer to the question of why C++ does not provide function template template parameters, but I imagine it has something to do with:

  • Any nontrivial change to templates will require complicated analysis to determine what changes need to be made to the standard text
  • This feature would be rarely used; after all, class template template parameters are mostly used in partial specializations, i.e., to dispatch on types that are themselves template specializations, and it's rare to want to do that with functions
  • It wouldn't make sense to support function template template parameters without also supporting a type of template parameter that would accept any non-template function (we can do this in C++17 with auto but this is obviously way too late for C++98 to have function template template parameters)
  • You can work around the lack of function template template parameters using class templates anyway (sort of like what we do with partial specializations).
  • Function overloading complicates things. In the presence of overloading, there's no way to unambiguously name a particular function template. Should one be provided? Or, do we take the point of view that an overload set is meant to be passed as a whole to a function template template parameter? What if overload resolution then selects a non-template overload? How do we even answer these design questions without having a compelling use case?

If you believe that you know how to address all these concerns, and in particular can provide a compelling argument for why we need this feature in the language despite the fact that it would be complicated and we can work around its absence using class template template parameters, feel free to write up a proposal for the standard.

Template function declared as void - code doesn't work?

Template parameters can be integers, for example:

template<int A, int B>
void bar()
{
std::cout << A+B << std::endl;
}

However, it seems like you want to parametrize your method on the types of the parameters, not on integer values. The correct template would be this:

template<typename T, typename U>
void foo(T a, U b)
{
std::cout << a+b << std::endl;
}

int main() {
bar<2,4>();
foo(2,4); // note: template parameters can be deduced from the arguments
return 0;
}

C++ template member function not declared in this scope

The correct way to invoke a member function is obj.function(...). You need:

cout << test.IsEmpty() << endl;   //error points to here

Why linker doesn't find my template function declared in class?

You must define template functions in a header file, otherwise the compiler won't know where to find the definition and only the declaration, which gives you a linker error like the one you have.

C++ template function compiles in header but not implementation

The problem you're having is that the compiler doesn't know which versions of your template to instantiate. When you move the implementation of your function to x.cpp it is in a different translation unit from main.cpp, and main.cpp can't link to a particular instantiation because it doesn't exist in that context. This is a well-known issue with C++ templates. There are a few solutions:

1) Just put the definitions directly in the .h file, as you were doing before. This has pros & cons, including solving the problem (pro), possibly making the code less readable & on some compilers harder to debug (con) and maybe increasing code bloat (con).

2) Put the implementation in x.cpp, and #include "x.cpp" from within x.h. If this seems funky and wrong, just keep in mind that #include does nothing more than read the specified file and compile it as if that file were part of x.cpp In other words, this does exactly what solution #1 does above, but it keeps them in seperate physical files. When doing this kind of thing, it is critical that you not try to compile the #included file on it's own. For this reason, I usually give these kinds of files an hpp extension to distinguish them from h files and from cpp files.

File: dumper2.h

#include <iostream>
#include <string>
#include <vector>

void test();
template <class T> void dumpVector( std::vector<T> v,std::string sep);
#include "dumper2.hpp"

File: dumper2.hpp

template <class T> void dumpVector(std::vector<T> v, std::string sep) {
typename std::vector<T>::iterator vi;

vi = v.begin();
std::cout << *vi;
vi++;
for (;vi<v.end();vi++) {
std::cout << sep << *vi ;
}
std::cout << "\n";
return;

}

3) Since the problem is that a particular instantiation of dumpVector is not known to the translation unit that is trying to use it, you can force a specific instantiation of it in the same translation unit as where the template is defined. Simply by adding this: template void dumpVector<int>(std::vector<int> v, std::string sep); ... to the file where the template is defined. Doing this, you no longer have to #include the hpp file from within the h file:

File: dumper2.h

#include <iostream>
#include <string>
#include <vector>

void test();
template <class T> void dumpVector( std::vector<T> v,std::string sep);

File: dumper2.cpp

template <class T> void dumpVector(std::vector<T> v, std::string sep) {
typename std::vector<T>::iterator vi;

vi = v.begin();
std::cout << *vi;
vi++;
for (;vi<v.end();vi++) {
std::cout << sep << *vi ;
}
std::cout << "\n";
return;
}

template void dumpVector<int>(std::vector<int> v, std::string sep);

By the way, and as a total aside, your template function is taking a vector by-value. You may not want to do this, and pass it by reference or pointer or, better yet, pass iterators instead to avoid making a temporary & copying the whole vector.

why a compiler can't find a template definition in .cpp

The C++ FAQ says it all:

  1. A template is not a class or a function. A template is a "pattern" that the compiler uses to generate a family of classes or functions.
  2. In order for the compiler to generate the code, it must see both the template definition (not just declaration) and the specific types/whatever used to "fill in" the template. For example, if you're trying to use a Foo, the compiler must see both the Foo template and the fact that you're trying to make a specific Foo.
  3. Your compiler probably doesn't remember the details of one .cpp file while it is compiling another .cpp file. It could, but most do not and if you are reading this FAQ, it almost definitely does not. BTW this is called the "separate compilation model."

http://www.parashift.com/c++-faq-lite/templates.html#faq-35.12

Why does a template have to be declared before every function that uses one?

You can think of it as part of the function signature. Maybe it's easier to see the connection if you write the full declaration on one line:

template<class T> T AddThree(T input)
{
return input + 3;
}

It is like how you need to declare the parameters for each function. You would not expect this to work:

std::string AddThree(std::string input)
{
return input + "3";
}

std::string SomethingElse(input)
{
// ...
}

Here, like with the template parameters, you need to declare input in the second function as well as the first. It is the scoping rules of the language :)

Member function template of class template can't find definition despite explicit instantiation present. Doesn't link

You should use the explicit instantion of the method template<typename T> template<typename U> Vector<T> Vector<T>::operator+(const Vector<U> & other) const (for all possible pairs of T and U) in addition to the explicit instantion of the Vector<T> class:

template Vector<int> Vector<int>::operator+(const Vector<short> & other) const;

Also you may simply move the definition of the Vector<T>::operator+ method to the header file.

In C++11 the extern template directive was introduced. You may use it in the header file for Vector<T> class (as @StoryTeller suggested in the comments):

extern template struct Vector<int>;

...to prevent the compiler from instantiating the Vector<T> class in every translation unit its specializations are used. Of course the same extern template directives may also be used for all Vector<T>::operator+ specializations explicitly instantiated in the .cpp file.



Related Topics



Leave a reply



Submit