In one of my projects I have a tree data structure, that might contain values of a generic type T.
In order to reduce compilation times and dependencies I tried to move implementation details from the tree node class Node to class NodeImpl. The internals of Node are private and should be only accessed at defined entry points, for example by the getName function.
Only people that are interested in using getName should include NodeImpl in their source files. This is how I think to reduce my compilation times and dependencies.
But for some reason the following three toys classes will not compile. It says no access to private members. What I'm doing wrong?
File main.cpp:
#include <iostream>
#include "Node.h"
#include "NodeImpl.h"
int main(int argc, char** args) {
Node<int> n("Test", 2);
std::cout << getName(n) << std::endl;
}
File Node.h:
#pragma once
#include <string>
template<typename T>
class NodeImpl;
template<typename T>
class Node {
public:
typedef T value_type;
Node(const std::string& name, const T& value) : name(name), value(value) {}
private:
std::string name;
T value;
friend class NodeImpl<T>;
};
File NodeImpl.h:
#pragma once
#include "Node.h"
template<typename T>
std::string getName(Node<T>& n);
template<typename T>
class NodeImpl {
NodeImpl(Node<T>& node) : mNode(node) {
}
Node<T>& mNode;
std::string name() {
return mNode.name;
}
friend std::string getName(Node<T>& n);
};
template<typename T>
std::string getName(Node<T>& n) {
auto x = NodeImpl<T>(n);
return x.name();
}
The warning produced by GCC gives insight:
warning: friend declaration 'std::__cxx11::string getName(Node<T>&)' declares a non-template function [-Wnon-template-friend]
friend std::string getName(Node<T>& n);
^
note: (if this is not what you intended, make sure the function template has already been declared and add <> after the function name here)
In other words, your class NodeImpl<int> was befriending a global function std::string getName(Node<int> &), which is unrelated to the function std::string getName<int>(Node<int> &) instantiated from the function template template <class T> std::string getName(Node<T> &) for T = int.
So the correct solution is this:
template<typename T>
class NodeImpl {
// ... as before ...
friend std::string getName<T>(Node<T>& n);
};
This [live example] shows that this solution indeed works.
Related
I have a class that it has a templated function
class DialogManager
{
public:
template<class T>
using TimeoutCallback = std::function<void (T)>;
DialogManager();
virtual ~DialogManager();
template<class T>
void SetTimeoutCallback(TimeoutCallback<T> callback);
int GetDialoge(T obj);
private:
TimeoutCallback mTimeoutCallback;
}
I get below error:
error: invalid use of template-name ‘DialogManager::TimeoutCallback’
without an argument list
I've changed code to
template<class T>
TimeoutCallback<T> mTimeoutCallback;
but I a got another compiler's error
error: data member ‘mTimeoutCallback’ cannot be a member template
example usage is
#include <DialogManager.h>
class foo()
{
public:
foo()
{
DialogManager* dialog = new DialogManager();
}
DialogManager* mDialogManager;
};
struct data
{
data() {}
};
int main()
{
Foo* f1 = new Foo();
auto a1 = f1->mDialogManager->GetDialoge(1);
auto b1 = f1->mDialogManager->GetDialoge("test");
Foo* f2 = new Foo();
data d;
auto a2 = f2->mDialogManager->GetDialoge(d);
auto b2 = f2->mDialogManager->GetDialoge(45.231);
return 0;
}
`
If you want to share the same template type in different parts of class, make it template class instead of template function.
#include <functional>
template<class T>
class DialogManager
{
public:
using TimeoutCallback = std::function<void (T)>;
DialogManager();
virtual ~DialogManager();
void SetTimeoutCallback(TimeoutCallback callback);
private:
TimeoutCallback mTimeoutCallback;
};
If you are writing a class header file, then you generally want to template the class and use the template type's name as a variable type within that file. Then, inside the coder file, you can define the methods similar to how you were in the code your provided. It looks like you fixed the first compilation error, and the second error seems to be because you are templating individual methods within the header file. Here is a basic example for templating a header file:
`template<typename T>
class Node{
private:
T m_entry;
Node<T>* m_nextNodePtr;
public:
T getEntry() const;
void setEntry(T newEntry);
//more
}`
And here is the header file's accompanying coder file:
`template<typename T>
T Node<T>::getEntry() const{
return m_entry;
}
template<typename T>
void Node<T>::setEntry(T newEntry){
m_entry = newEntry;
return;
//more
}`
Hope this helps!
Your mTimeoutCallback is a template member variable; resolving the using, it's a
template<class T>
std::function<void (T)> mTimeoutCallback;
Unfortunately (a) template variables are available only starting from C++14 (and you tagged C++11) and (b) a template variable can be member of a class/struct only if static.
So your errors.
I have done explicit specializations before, I just can't figure out why this does not work:
StringUtils.hpp
#ifndef AYC_STRINGUTILS_HPP
#define AYC_STRINGUTILS_HPP
#include <string>
class StringUtils {
public:
template <typename T>
static std::string toString(const T& t);
};
#include "StringUtils.tpp"
#endif //AYC_STRINGUTILS_HPP
StringUtils.tpp
#include "StringUtils.hpp"
template<typename T>
std::string StringUtils::toString(const T& t) {
return std::to_string(t);
}
template<>
std::string StringUtils::toString<std::string>(const std::string& t) {
return t;
}
The errors I get are linker errors complaining about multiple definitions of the function toString.
Many files in the project use #include "StringUtils.hpp".
How can I try to fix this error? Is something wrong in the class StringUtils?
In addition to the solution provided in the answer by Brian, you can declare the specialization in the .hpp/.tpp file and define it in a .cpp file.
StringUtils.hpp file:
#ifndef AYC_STRINGUTILS_HPP
#define AYC_STRINGUTILS_HPP
#include <string>
class StringUtils {
public:
template <typename T>
static std::string toString(const T& t);
};
// Generic implementation.
template<typename T>
std::string StringUtils::toString(const T& t) {
return std::to_string(t);
}
// Declation of the specialization.
template<>
std::string StringUtils::toString<std::string>(const std::string& t);
#endif //AYC_STRINGUTILS_HPP
StringUtils.cpp file:
#include "StringUtils.hpp"
// Definition of the specialization.
template<>
std::string StringUtils::toString<std::string>(const std::string& t) {
return t;
}
Test program:
#include <iostream>
#include "StringUtils.hpp"
int main()
{
std::string a("test");
std::cout << StringUtils::toString(a) << std::endl;
std::cout << StringUtils::toString(10) << std::endl;
}
Output of the test program:
test
10
An explicit (full) specialization of a function template is subject to the one-definition rule, so StringUtils::toString<std::string> must not be defined in multiple translation units. You can solve this problem by declaring it inline.
Template function specialization is almost always the wrong answer.
Classes are poor namespaces.
Simply overload instead of specialize.
namespace StringUtils {
template <typename T>
std::string toString(const T& t){
using std::to_string;
return to_string(t);
}
inline std::string toString(std::string s){ return std::move(s); }
}
overload resolution does what you want, and it permits efficient signature variation (like above, where I take s by-value, which could avoid an extra heap allocation).
Also note I enabled ADL extension of to_string for custom classes. Simply overload to_steing(X) in X's namespace and StringUtils::toString(X) finds it.
Your immediate problem is you need to mark the specialization inline.
Could you please advise why I am getting the error in the code below?
error: cannot define member function ‘Test<int>::Printer::Print’ within ‘Test<int>’
I am using gcc version 8.1.1 and compile the code as g++ -std=c++11.
Although, if I move the definition of function Print under the definition of struct Printer (i.e. making it inline implicitly), the compiler does not produce any error.
#include <iostream>
template <typename Type>
struct TestBase {
struct Printer {
template <typename T>
void Print(const T& t) {
std::cout << t << std::endl;
}
};
};
template <typename Type>
struct Test;
template<>
struct Test<int> : public TestBase<int> {
struct Printer : public TestBase<int>::Printer {
template <typename T>
void Print(int i, const T& t);
};
template <typename T>
void Printer::Print(int i, const T& t) {
std::cout << i << t << std::endl;
}
};
int main() {
Test<int> t;
}
UPDATE:
Brian pointed out the exact reason why it is the case: "... A member function definition that appears outside of the class definition shall appear in a namespace scope enclosing the class definition..."
Brian not only answered the main question that started this topic but also an additional question that I asked in the comment to the accepted answer of him.
[class.mfct]/1, emphasis mine:
... A member function definition that appears outside of the class definition shall appear in a namespace scope enclosing the class definition. ...
An enclosing class scope is thus not an allowed location for the definition.
Why are we suppose to use template parameters at front of every function even if we are not using deduced template parameters in the function. As we can see that i am not using template parameter _T in printP() function (around 30) then why it is required to include template syntax at front of this function.
NOTE: This is very simplified version of my big class, and it might look silly because it is very small but, consider a situation where you are using template for only few [2-3] function of your class but you are bound to type (even copy past) this lengthy template syntax at front of every function but i am asking why??.
Is there any way to get of this
#include <iostream>
#include <cstring>
#include <fstream>
using namespace std;
template<typename _T>
class Thing{
int _p;
_T _k;
public:
Thing(int p, _T k):_p(p),_k(k){}
void printK();
void printP();
};
template<typename _T>
void Thing<_T>::printK(){
cout << _k << endl;
}
template<typename _T>
void Thing<_T>::printP(){
cout << _p << endl; // as we can see that i am not using template paramerter "_T"
} // here in this function then why it is required to include template syntax
int main()
{
Thing<int> a(1,2);
a.printK();
a.printP();
}
Because the function PrintK is member of template class Thing<_T>. For a member function definition outside the class, the function name also includes class name(to which it belongs, here it belongs to Thing), since Thing is template, so function name requires you to provide template parameter (T here).
e.g.
Function definition outside class requires the following syntax
**
return type class name:: function name (argument list)
*
Here class (Thing) is template class, so its name will also require type (like Thing<_T>).
I hope you got my point.
Its usually a good idea to restrict the members and functions of a template class to those that are dependent on the template parameters. Non-dependent members and functions can be put in a separate non=template class (is there a better name?). For example:
#include <iostream>
using namespace std;
class ThingBase
{
public:
ThingBase(int p)
: _p(p)
{
}
void printP();
protected:
int _p;
};
void ThingBase::printP(){
cout << _p << endl;
}
template<typename _T>
class Thing : public ThingBase {
_T _k;
public:
Thing(int p, _T k)
: ThingBase(p),
_k(k){}
void printK();
};
template<typename _T>
void Thing<_T>::printK(){
cout << _k << endl;
}
int main()
{
Thing<int> a(1,2);
a.printK();
a.printP();
}
When I try to compile this with Clang
template<class T>
struct Field
{
char const *name;
Field(char const *name) : name(name) { }
};
template<class Derived>
class CRTP { static Field<Derived> const _field; };
class Class : public CRTP<Class> { };
Field<Class> const CRTP<Class>::_field("blah");
int main() { }
I get
error: template specialization requires 'template<>'
Field<Class> const CRTP<Class>::_field("blah");
~~~~~~~~~~~ ^
I don't understand the error at all. What is wrong with my definition of _field and how do I fix it?
(Note that the arguments to _field are not necessarily the same for all subclasses.)
For the compiler to identify this as a template specialization (e.g. to be able to check the syntax), you need the template keyword:
template<>
Field<Class> const CRTP<Class>::_field("blah");
Its brackets are empty as all template parameters are specialized, but you cannot just leave it away.
The error says exactly what is missing. template<> is missing before that line.
template<>
Field<Class> const CRTP<Class>::_field("blah");
Note, however, that your typing of Field<Class>, if unique, could be used to construct all instances of Field<Class> with a given string.
template<typename T>
struct field_trait;
template<class T>
struct Field
{
char const *name;
Field() : name(field_trait<T>::get_name()) {}
};
template<class Derived>
class CRTP { static Field<Derived> const _field; };
template<class Derived>
class CRTP<Derived>::_field;
class Class;
template<>
struct field_traits<Class> {
static const char* get_name() { return "blah"; }
};
class Class : public CRTP<Class> { };
int main() { }
which means that every instance of Field<Class> always has the name "blah".
One question I would have is, do you really need storage for said Field<Class> to actually have a pointer to a string, and if so does it need to be unique, and if so does it need to be "bare"? Because figuring out where the static instance exists is somewhat annoying.
Together with field_traits above:
template<class Derived>
class CRTP { static Field<Derived>& field() const { static Field<Derived> _field( field_traits<Derived>::get_name()); return _field; };
this moves the problem of "where is the _field stored" to being the compilers problem. And it is initialized by the contents of field_traits<T>::get_name().
A static data member must have both a declaration and a definition. If this was a plain class it would look like this:
// header:
class C {
static int i;
};
// source:
int C::i = 3;
Templates aren't ordinarily defined in source files, so the code would look something like this:
// header:
template <class T>
class C {
static int i;
};
template <class T>
int C<T>::i = 3;
In your code, you don't have the definition of the static data member. That's okay if you don't use it. But the code that the compiler is complaining about defines a static data member for CRTP<Class>; that's a specialization (because it's not applicable to all instantiations of CRTP, just to this one), and the compiler is saying that you have to tell it that it's a specialization. So do as you're told:
template <>
Field<Class> const CRTP<Class>::_field("blah");
or, to write the non-specialized template version, use the usual template syntax:
template <class T>
Field<T> const CRTP<T>::_field("blah");