The problem I am struggling with is the declaration of specialized template function inside template class (I keep class declaration in header file and define member functions in associated .C file).
I have template class representing Points. The header file is presented below:
//...
template<typename T, int dim=3> // T - coords. type, int dim - no. of dimensions
class Point {
public:
// ...
// function below sets val at the given position in array m_c and returns reference
template<int position> Point& set(T val);
private:
T m_c[dim]; // coordinates
};
//...
definition of function set is placed in .C file:
template<typename T, int dim> template<int position> Point<T, dim>& Point<T, dim>::set(T val){
// ...
return *this;
}
As I understand this is the most general form of its definition.
In main function I create Point with float as T and try to set some values in the array:
int main(int argc, char** argv) {
Point<float> p1;
p1.set<0>(3).set<1>(3.6).set<2>(3);
//...
}
In order to make this possible with definition of the member functions of template outside header file I need to inform compiler about specialization in .C file:
template class Point<float>;
and I need as well to declare usage of set function, which I try to accomplish this way
(and this piece of code is the problem):
template<> template<int> Point<float>& Point<float>::set(float);
That unfortunately doesn't do the job and I get errors:
/tmp/ccR7haA5.o: In function `main':
.../pdim.C:32: undefined reference to `Point<float, 3>& Point<float, 3>::set<0>(float)'
.../pdim.C:32: undefined reference to `Point<float, 3>& Point<float, 3>::set<1>(float)'
.../pdim.C:32: undefined reference to `Point<float, 3>& Point<float, 3>::set<2>(float)'
I would really appreciate an explanation from someone who may know how to cope with this problem. Thanks.
In order to provide the definition of a function template specialization in a different TU, you need an explicit instantiation declaration:
[Point.hpp]
template<typename T, int dim=3>
struct Point
{
template<int position> Point& set(T val);
};
// `extern` makes this an explicit instantiation _declaration_
extern template Point<float,3>& Point<float,3>::set<0>(float);
extern template Point<float,3>& Point<float,3>::set<1>(float);
extern template Point<float,3>& Point<float,3>::set<2>(float);
[Point.cpp]
#include <iostream>
#include "Point.hpp"
template<typename T, int dim>
template<int position>
Point<T,dim>& Point<T,dim>::set(T val)
{
// note: non-standard macro
std::cout << __PRETTY_FUNCTION__ << std::endl;
return *this;
}
// no `extern`: this is an explicit instantiation _definition_
// which instantiates the function template, and therefore requires the definition
// to be available in this TU
template Point<float,3>& Point<float,3>::set<0>(float);
template Point<float,3>& Point<float,3>::set<1>(float);
template Point<float,3>& Point<float,3>::set<2>(float);
[main.cpp]
#include "Point.hpp"
int main()
{
// in this TU, there's no definition for the function template
// hence, it cannot be instantiated
// however, we can use the explicit instantiations
Point<float,3>().set<0>(0);
Point<float,3>().set<1>(0);
Point<float,3>().set<2>(0);
// does not compile (linker error):
//Point<int,3>().set<0>(0);
//Point<float,4>().set<0>(0);
//Point<float,3>().set<4>(0);
}
Related
I have such test.hpp:
#include <cstring>
#include <cassert>
#include <map>
template <typename T, typename Tag>
struct Boo {
using ElementType = T;
static const char name[];
};
struct CFG_ELM_NAME__Pref1 {};
using Pref1 = Boo<int, CFG_ELM_NAME__Pref1>;
struct Foo {
template <typename CfgElm>
void get() const {
auto it = cache_.find(CfgElm::name);
assert(it != cache_.end());
}
Foo();
private:
struct CmpCStr final {
bool operator()(const char *a, const char *b) const {
return std::strcmp(a, b) < 0;
}
};
using PrefCacheMap = std::map<const char *, int, CmpCStr>;
PrefCacheMap cache_;
};
and use it like this (test2.cpp):
#include "test.hpp"
void f()
{
Foo foo;
foo.get<Pref1>();
}
and initialize it like this (test.cpp):
#include "test.hpp"
template <> const char Pref1::name[] = "Pref1";
Foo::Foo()
{
cache_.insert(std::make_pair(Pref1::name, 17));
}
This is reduced example, so Foo::get do nothing.
clang produces such warning for this code:
clang++ -Wall -Wextra -std=c++11 test.cpp test2.cpp
In file included from test2.cpp:1:
./test.hpp:15:35: warning: instantiation of variable 'Boo<int,
CFG_ELM_NAME__Pref1>::name' required here, but no definition is
available [-Wundefined-var-template]
auto it = cache_.find(CfgElm::name);
^
test2.cpp:6:6: note: in instantiation of function template specialization
'Foo::get<Boo<int, CFG_ELM_NAME__Pref1> >' requested here
foo.get<Pref1>();
^
./test.hpp:7:21: note: forward declaration of template entity is here
static const char name[];
^
./test.hpp:15:35: note: add an explicit instantiation declaration to
suppress this warning if 'Boo<int, CFG_ELM_NAME__Pref1>::name' is
explicitly instantiated in another translation unit
auto it = cache_.find(CfgElm::name);
This codes does compiles and links without problems.
The only problem is warning.
I have no idea how to suppress it.
I found this question explicit instantiation of static variable of a template class in different translation units , but I can not use provided solution, because of I don't know template arguments.
I can not write: template<> int Boo<Type1, Type2>::name;
because of the whole idea is to use my code like this: foo.get<Pref1>(), without explicitly pointing that Pref1 is Boo<int, CFG_ELM_NAME__Pref1>.
So anybody know how to suppress warning, without turn of such warning for whole project via -Wno-undefined-var-template?
You should add Boo::name template definition in the same header file:
static const char * name;
}; // End Boo
template<typename T, typename Tag>
const char * Boo<T, Tag>::name{};
Update: now it is clarified that you are trying to write specialization for name in some translation unit after instantiating it in header file. This will require some trickery. You need to declare specialization in header file before instantiating it and probably use external template and explicitly instantiate it in the same translation unit as name:
// header
template <typename T, typename Tag> struct Boo {
using ElementType = T;
static const char * name;
};
struct CFG_ELM_NAME__Pref1 {};
// indicate that name for this specialization exists elsewhere
template<> const char * Boo<int, CFG_ELM_NAME__Pref1>::name;
// indicate that template is defined somewhere
extern template struct Boo<int, CFG_ELM_NAME__Pref1>;
using Pref1 = Boo<int, CFG_ELM_NAME__Pref1>;
// test.cpp
// definition will be present only in this translation unit
template<> const char * Boo<int, CFG_ELM_NAME__Pref1>::name{"Pref1"};
// explicit instantiation
template struct Boo<int, CFG_ELM_NAME__Pref1>;
online compiler
I am having problems passing lambda expressions as a parameter to a method of a template class.
If I compile the following code:
main.cpp:
#include "a.h"
int main(int argc, char** argv) {
A<int> o;
o.a([&](){ });
}
a.h:
template <typename T>
class A {
public:
template <typename Lambda>
void a(const Lambda& f) {
f();
}
};
It works fine.
However I do not want to have my implementation of class A in a.h, I want to put the code it in a separate cpp file.
So I now have
main.cpp:
Unchanged
a.h:
template <typename T>
class A {
public:
template <typename Lambda>
void a(const Lambda& f);
};
a.cpp:
#include "a.h"
template <typename T>
template <typename Lambda>
void A<T>::a(const Lambda& f) {
f();
}
template class A<int>;
I now get the following error:
In file included from test.cpp:1:0:
a.h:7:7: error: 'void A<T>::a(const Lambda&) [with Lambda = main(int, char**)::__lambda0; T = int]', declared using local type 'const main(int, char**)::__lambda0', is used but never defined [-fpermissive]
void a(const Lambda& f);
^
Why is that? How can I fix it?
I noticed if I put everything in the same cpp file, it works. The problem only occurs when I want to split it this way which is necessary to keep my code organized.
Thank you for your answer
This is very strange because even this works:
main.cpp
#include "a.h"
int main(int argc, char** argv) {
A<int> o;
o.a([&](){ });
}
template <typename T>
template <typename Lambda>
void A<T>::a(const Lambda& f) {
f();
}
template class A<int>;
a.h
template <typename T>
class A {
public:
template <typename Lambda>
void a(const Lambda& f);
};
This works.
For some reason,
It just won't work when I separate the main() and the method implementation in two distinct cpp files.
Why?
OK I think I have found an explanation.
When you split the code of a template class between a .h and a .cpp,
You need to make template instantiation explicit.
The problem is that lambda expressions have no specified type, therefore it is not possible to write a template explicit instance of that function for a lambda type.
What I want is impossible:
Is it possible to explicitly specialize template to match lambda?
Generally you can't really put the implementation of template functions/classes in a .cpp file. The reason is that when the .cpp file is compiled separately the compiler has no way to know what template parameters will be passed to it. So I think for your situation it's best to leave everything in a header file.
As others have said it is not possible to separate a template class from its implementation in header and cpp files. However, if the class itself is not a template but one of the member functions accepts a lambda then you can separate the implementation. To do this, do not use template to declare the lambda. Use std::function instead.
Header file a.h.
#include <functional>
namespace xx {
struct A {
void square(int num, std::function<void(int)>consumer);
};
}
CPP file a.cpp.
namespace xx {
void A::square(int num, std::function<void(int)>consumer) {
consumer(num * num);
}
}
Use this as follows.
#include "a.h"
#include <assert.h>
int main() {
xx::A a;
a.square(10, [](int result) {
assert(result == 100);
});
return 0;
}
I would like to define the explicit specialization of a template function in a cpp file. Is that possible? To be more concrete, I have the following code, which compiles without errors:
//class.h
class myclass
{
public:
/* Constructor */
myclass();
/* Trigger fcn */
template<typename T> T Trigger(T rn);
private:
/* Specializations of the templated Trigger(...) function */
template<> int Trigger<int>(int rn)
{
int_do(rn);
}
template<> double Trigger<double>(double rn)
{
double_do(rn);
}
}
However, I having the definitions in the header file looks weird for me, so I would like to separate the definitions from the declarations, something like this:
//class.h
class myclass
{
public:
/* Constructor */
myclass();
/* Trigger fcn */
template<typename T> T Trigger(T rn);
private:
/* Specializations of the templated Trigger(...) function */
template<> int Trigger<int>(int rn);
template<> double Trigger<double>(double rn);
}
and:
//class.cpp
/* Specializations of the templated Trigger(...) function */
template<> int myclass::Trigger<int>(int rn)
{
int_do(rn);
}
template<> double myclass::Trigger<double>(double rn)
{
double_do(rn);
}
Is there any way to to this?
Your only error is declaring the specialisations inside the class. Declare them in the header, but outside the class:
class myclass
{
public:
myclass();
template<typename T> T Trigger(T rn);
};
/* Specializations of the templated Trigger(...) function */
template<> int myclass::Trigger<int>(int rn);
template<> double myclass::Trigger<double>(double rn);
and then you can define them in a source file, exactly as you have done.
Note that your first snippet doesn't compile (unless your compiler has a non-standard extension), since the specialisations can't be declared inside the class.
In addition to earlier answer regarding the positioning of the specialization:
You can define template functions in a .cpp file. This also means that they can only be instantiated within this translation object/file.
In your case, the partial specialization is declared in the .h, and other compilation units will expect their existence.
For example in
file tmp.h:
#include <iostream>
class T {
public:
template <typename T> void foo(const T& t)
{
std::cout << "generic foo" << std::endl;
}
};
// forward declaration of specialization
template<> void T::foo(const double& t);
file tmp.cpp:
#include "tmp.h"
template <> void T::foo(const double& t)
{
std::cout << "double foo" << std::endl;
}
file main.cpp:
#include "tmp.h"
int main(int argc, const char** argv)
{
T t;
t.foo(1.0);
t.foo(1);
return 0;
}
Now compiling both .cpp files will work fine:
g++ main.cpp tmp.cpp -o tmp
./tmp
double foo
generic foo
without the definition of the specialization:
g++ main.cpp -o tmp
/tmp/ccpCJr3B.o: In function `main':
main.cpp:(.text+0x1f): undefined reference to `void T::foo<double>(double const&)'
collect2: error: ld returned 1 exit status
I am new to programming in C++ and I have encountered a problem that I cannot seem to solve when enforcing separate compilation. I am trying to specialize my class tokenize to add a dtor for a specific type (istream). I have the following:
#ifndef __TOKENIZER_H__
#define __TOKENIZER_H__
#include <fstream>
#include <string>
template <class T>
class base {
// ... some methods/member variables.
};
template <class T>
class tokenizer : public base<T> {
public:
tokenizer(T &in);
};
template <>
class tokenizer<std::ifstream> : public base<std::ifstream> {
public:
tokenizer(std::ifstream &in);
~tokenizer();
};
#endif
... and:
#include "tokenizer.h"
#include <fstream>
#include <iostream>
#include <locale>
using std::ifstream;
using std::istream;
using std::string;
// [BASE]
// ... code for those functions.
// [TOKENIZER]
// See header file.
template <class T>
tokenizer<T>::tokenizer(T &in) : base<T>(in) { }
// See header file.
template <>
tokenizer<ifstream>::tokenizer(ifstream &in) : base<ifstream>(in) { }
// See header file.
template <>
tokenizer<ifstream>::~tokenizer() {
delete &(base<ifstream>::in);
}
// Intantiating template classes (separate compilation).
template class base<std::ifstream>;
template class base<std::istream>;
template class tokenizer<std::ifstream>;
template class tokenizer<std::istream>;
... however I get the following error:
tokenizer.cc:62: error: template-id ‘tokenizer<>’ for ‘tokenizer<std::basic_ifstream<char, std::char_traits<char> > >::tokenizer(std::ifstream&)’ does not match any template declaration
tokenizer.cc:66: error: template-id ‘tokenizer<>’ for ‘tokenizer<std::basic_ifstream<char, std::char_traits<char> > >::~tokenizer()’ does not match any template declaration
I am compiling with g++. If someone can kindly point out what I am missing and a possible explanation then that would be fantastic. I am confused how templates work with separate compilation (defns/decl separated).
[temp.expl.spec]/5 states:
Members of an explicitly specialized class template are defined in the same manner as members of normal classes, and not using the template<> syntax. The same is true when defining a member of an explicitly specialized member class. However, template<> is used in
defining a member of an explicitly specialized member class template that is specialized as a class template.
It also provides the following example (I'll only quote some excerpts):
template<class T> struct A {
template<class U> struct C { };
};
template<> struct A<int> {
void f(int);
};
// template<> not used for a member of an
// explicitly specialized class template
void A<int>::f(int) { /∗ ... ∗/ }
template<> template<class U> struct A<char>::C {
void f();
};
// template<> is used when defining a member of an explicitly
// specialized member class template specialized as a class template
template<>
template<class U> void A<char>::C<U>::f() { /∗ ... ∗/ }
As far as I know, once you've explicit specialized a class template, you've created a "normal class". It's obviously not a template any more (you cannot create classes from the specialization), but a type with some <..> in its name.
In your case, that just means leave out the template<> before
// See header file.
//template <>
tokenizer<ifstream>::tokenizer(ifstream &in) : base<ifstream>(in) { }
// See header file.
//template <>
tokenizer<ifstream>::~tokenizer() {
delete &(base<ifstream>::in);
}
With regard to your request on clarification of the combination of separate compilation with templates:
When you use a class template to create an object (e.g. std::vector<int> v) or call a function template (e.g. std::sort(begin(v), end(v))), you're dealing with specializations of the templates. std::vector<int> is a specialization of the class template std::vector.
When a specialization is required in a TU, it might be necessary to produce it from the class template. This is called instantiation. An explicitly specialized template won't be instantiated implicitly (it already is specialized). That is, your specialization tokenizer<ifstream> doesn't have to be instantiated in any TU.
Templates itself don't work with separate compilation for these reasons. However, you can use explicit instantiations and explicit specializations to provide the benefits of separate compilation for specializations of templates. For example:
[header.hpp]
template<class T> void foo(T);
extern template void foo<int>(int);
[impl.cpp]
#include "header.hpp"
template<class T> void foo(T) { return T{} };
template void foo<int>(int); // force instantiation
[main.cpp]
#include "header.hpp"
int main()
{
foo<int>(42); // no instantiation will occur
}
In main.cpp we couldn't instantiate the definition of foo, as the definition is not available. We could instantiate the declaration. There's also an explicit instantiation declaration, which prevents any implicit instantiation. In another TU (impl.cpp), we did instantiate foo<int> via an explicit instantiation definition. This requires the definition of f to exist and instantiates the definition. The rest is similar to normal functions: We have two declarations and one definition.
Similarly, for class templates: If the definition of a class is required in a TU, we need to either instantiate the template or we need to have an explicit specialization (an explicit instantiation definition is not possible here AFAIK). This is exactly the OP's example.
If the definition of the class is not required, we can use something similar to the PIMPL idiom:
[header.hpp]
template<class T>
class foobar;
struct s
{
foobar<int>* p;
void f();
}
[impl.cpp]
#include "header.hpp"
template<class T> class foobar { int i; }
void s::f() { p = new foobar{42}; }
[main.cpp]
int main()
{
s obj;
obj.f();
}
I'm seeing an error related to templates (compiler is Visual Studio 2012) that I don't understand. Here's the code, boiled down to the essentials:
// Templated class - generic
template <typename T>
class Test
{
public:
void WorksFine() {} // Comiples and works as expected at runtime
void Problem();
};
// Templated class - expicit specialization for T = int.
template <>
class Test<int>
{
public:
void WorksFine() {} // Comiples and works as expected at runtime
void Problem();
};
// The definition below compiles and works fine at runtime.
template<typename T> void Test<T>::Problem() {}
// The definition below gives error C2910.
template<> void Test<int>::Problem() {printf("In Test::Problem(int instantiation)\n");}
For the WorksFine method, the function definition is inside the explicitly specialized class definition, and everything is fine. But for the Problem method, when I define the method outside the explicitly specialized class definition, I get error C2910
Why is this? Error C2910 indicates that the problem is that Test::Problem() is already defined. But it is not defined inside the class...there is no function definition only a declaration.
It seems pretty lame to be able to do something or not depending on where you choose to put the function definition, which I always though was more of a style/syntax decision, not a functionality/semantics decision. Am I missing something?
You don't need the template<>. Just write:
void Test<int>::Problem() {printf("In Test::Problem(int instantiation)\n");}
The template<> syntax on a member specialization is required where explicitly instantiating a member on its own; it is omitted when defining a member of an already existing specialization.
template<typename T> struct X { static int i; };
template<> int X<int>::i = 0; // member instantiation, uses template<>
template<typename T> struct Y { static int i; };
template<> struct Y<int> { static int i; } // template specialization
int Y<int>::i = 0; // no template<>
You don't need template anymore in the explicit function definition: void Test<int>::Problem() {printf("In Test::Problem(int instantiation)\n");}
In this case g++ gives a slightly better error message error: template-id 'Problem<>' for 'void Test<int>::Problem()' does not match any template declaration
Try this:
// The definition below gives error C2910.
void Test<int>::Problem()
{
printf("In Test::Problem(int instantiation)\n");
}
int main()
{
Test<int> hey;
hey.Problem();
return 0;
};