Background
I have a non templated class, called library and a templated class library_file, which is intended to have a template parameter F for different file types (like std::fstream or QFile and so on) to save/load a library on.
Assumption
So I put a forward declaration of library_file before my definition of library and a friend declaration in the last one. Because, since library_file includes library I would otherwise be in a dependency circle.
Problem
The friend declaration fails with
In file included from /Users/markus/Entwicklung/cmake_Spielwiese/library.cpp:4:
/Users/markus/Entwicklung/cmake_Spielwiese/library.h:12:12: error: C++ requires a type specifier for all declarations
friend library_file;
~~~~~~ ^
/Users/markus/Entwicklung/cmake_Spielwiese/library.h:12:12: error: friends can only be classes or functions
2 errors generated.
Code
/*! #file library.h
* */
#ifndef CMAKE_CPP_SPIELWIESE_LIBRARY_H
#define CMAKE_CPP_SPIELWIESE_LIBRARY_H
template <typename F>
class library_file;
class library {
template <typename F>
friend library_file;
};
#endif //CMAKE_CPP_SPIELWIESE_LIBRARY_H
/*! #file library_file.h
* */
#ifndef CMAKE_CPP_SPIELWIESE_LIBRARY_FILE_H
#define CMAKE_CPP_SPIELWIESE_LIBRARY_FILE_H
#include "library.h"
template <typename F>
class library_file {
F file;
library l;
};
#endif //CMAKE_CPP_SPIELWIESE_LIBRARY_FILE_H
/*! #file main.cpp
* */
#include <fstream>
#include "library.h"
#include "library_file.h"
int main() {
library_file<std::fstream> f;
return 0;
}
/*! #file library.cpp
* */
#include "library.h"
The correct declaration of a template friend class is :
template<class F> friend class library_file;
see Class template with template class friend, what's really going on here?
You forgot the class keyword in library_file.h, it compiles just fine after that
#ifndef CMAKE_CPP_SPIELWIESE_LIBRARY_H
#define CMAKE_CPP_SPIELWIESE_LIBRARY_H
template <typename F>
class library_file;
class library {
template <typename F>
friend class library_file;
};
#endif //CMAKE_CPP_SPIELWIESE_LIBRARY_H
Related
Newbie to C++ so please forgive me. I'm trying to write a simple template class to implement the Stack data structure functionality. I've read through this question to understand how template declaration and implementation works: Why can templates only be implemented in the header file?. But I would like to use the solution where class declaration and implementation are separate (not defined inline in .h).
MyStack.h:
#include "StackNode.h"
#include "MyStack.tpp"
template <typename T>
class MyStack
{
private:
StackNode<T> *top;
public:
MyStack();
virtual ~MyStack();
};
MyStack.tpp:
#include "MyStack.h"
template <typename T>
MyStack<T>::MyStack()
: top(nullptr)
{
}
template <typename T>
MyStack<T>::~MyStack()
{
}
main.cpp:
#include "MyStack.h"
int main() {
MyStack<int> myStack;
return 0;
}
However compiling the above gives this error:
../src/MyStack.tpp:11:1: error: 'MyStack' does not name a type
../src/MyStack.tpp:18:1: error: 'MyStack' does not name a type
I know I'm missing something, but don't understand what. Thanks!
The problem is that you are including the .tpp file, which contains the class method definitions, before the class has actually been declared yet. You need to move that #include statement below the class declaration instead.
Also, the .tpp file should not be #include'ing the .h file that originally #include'd it. In fact, that won't work correctly since your .h file is missing header guards to prevent multiple inclusions within the same translation unit (ie, main.cpp in this case). So, you would end up with MyStack.h which includes MyStack.tpp which includes MyStack.h again, which causes an error when it tries to re-declare things that are already declared. Always declare a header guard in your .h files.
Try this instead:
MyStack.h:
#ifndef MyStack_H
#define MyStack_H
#include "StackNode.h"
template <typename T>
class MyStack
{
private:
StackNode<T> *top;
public:
MyStack();
virtual ~MyStack();
};
#include "MyStack.tpp"
#endif
MyStack.tpp:
template <typename T>
MyStack<T>::MyStack()
: top(nullptr)
{
}
template <typename T>
MyStack<T>::~MyStack()
{
}
I am trying to create a vector class that looks something like this:
template <typename T>
class Vector
{
.
.
.
};
#include "vector.cpp"
However, when I start writing my functions in "vector.cpp", CLion complains that I have duplicate functions. How do I work around this? I believe in NetBeans, I can add vector.h & vector.cpp to a folder called "Important Files" which would fix the problem. I am not sure what the equivalent in CLion is.
General Design of a template
example.h
#ifndef EXAMPLE_H
#define EXAMPLE_H
// Needed includes if any
// Prototypes or Class Declarations if any
template<class T> // or template<typename T>
class Example {
private:
T item_;
public:
explicit Example( T item );
~Example();
T getItem() const;
};
#include "Example.inl"
#endif // EXAMPLE_H
Example.inl
// Class Constructors & Member Function implementations
template<class T>
Example<T>::Example( T item ) : item_(item) {
}
template<class T>
Example<T>::~Example() {
}
template<class T>
T Example<T>::getItem() const {
return item_;
}
Example.cpp
#include "Example.h"
// Only need to have the include here unless if
// the class is non template or stand alone functions
// are non-template type. Then they would go here.
A file contains template class A and template class B. A is friend of B.
I want to separate them into different files. How to deal with it?
A.h
#if !defined(FILE_A_H)
#define FILE_A_H
template<class T>
class A
{
template<class> friend class B;
// ...
};
#endif
B.h
#if !defined(FILE_B_H)
#define FILE_B_H
template<class T> class B { /* ... */ };
#endif
Note that if the name of the class that is used in the friend declaration is not yet declared, it is forward declared on the spot (see http://en.cppreference.com/w/cpp/language/friend).
Further details:
Why can't I separate the definition of my templates class from its declaration and put it inside a .cpp file?
Why do I get linker errors when I use template friends?
Templates, C++FAQ
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 have a class that wraps logic for matrix management. Meanwhile I have a set of methods specialized in handling some important time spending matrix manipulation operations (like LU factorization and so on).
The class uses the functions defined in that file. That file needs to have access to that class' elements. I need to make those specialized methods friends of the above-mentioned class. This causes me to include one header in each other header.
My problem
The situation I described before is coded here as follows. The first code refers to mat.hpp.
#ifndef MAT_HPP
#define MAT_HPP
#include "operations.hpp"
namespace nsA {
template <typename T>
// Templated class because matrices can be real or complex
class mat {
// Members...
friend template <typename U>
void exec_lu(const mat<U>& in, const mat<U>& out);
// Members...
} /* class end */
}
#endif
#endif
The second file is operations.hpp
#ifndef OPERATIONS_HPP
#define OPERATIONS_HPP
#include "mat.hpp"
namespace nsA {
namespace nsB {
template <typename T>
void exec_lu(const mat<T>& in, const mat<T>& out);
}
}
#endif
The problem is that the compiler starts complaining.
Note
Consider please that if I comment the friend declaration in mat.hpp but leave the inclusions, the compiler tells me that in 'operations.hpp' type mat is not defined!
If also comment the inclusion in mat.hpp and keep friend declaration commented as well, the compiler is ok!
How to work on this?
Thankyou
You can do this by just adding a couple of forward declarations... But you can even do better with a bit more code:
template <typename T> class mat;
template <typename T> void exec_lu( const mat<T>&, const mat<T>& );
template <typename T>
class mat {
friend void exec_lu<T>( const mat<T>&, const mat<T>& );
};
template <typename T>
void exec_lu( const mat<T>& a, const mat<T>& b ) { ... }
The main difference between this approach and yours (other than fixing the syntax limitations) is that in this approach a single instantiation of exec_lu is granted access to mat<T>, in particular the instantiation that needs access to it. In your code (after fixing), all specializations of exec_lu would have access to any specialization of mat (i.e. exec_lu<int> could access mat<int> private members, but also mat<double>...) and you probably don't want that.
For a longer description of the different options to declare friends of a template, read this answer to a related question.
To get this to work, you need to forward declare the class. It looks like this:
The file mat.hpp:
#ifndef MAT_HPP
#define MAT_HPP
namespace nsA {
template <typename T>
class mat;
namespace nsB {
template <typename T>
void exec_lu(const mat<T>& in, const mat<T>& out);
}
template <typename T>
class mat {
friend void exec_lu(const mat<T>& in, const mat<T>& out);
};
}
#endif
And the file operations.hpp:
#ifndef OPERATIONS_HPP
#define OPERATIONS_HPP
#include "mat.hpp"
namespace nsA { namespace nsB {
template <typename T>
void exec_lu(const mat<T>& in, const mat<T>& out);
}}
#endif
Your original declaration gives out more friendship than mine. Mine only grants friendship to the function that matches the typename of the class.