I'm trying to create a class, which will contain two pairs of template functions: one for char and one for wchar_t. I wrote the following code, but it couldn't be built because linker cannot find realizations of functions. I think the problem is that linker thinks the functions in the class are not the instantiations of template ones.
How can I define the functions needed?
template<typename T>
int func1(const T* szTarget)
{
...
}
template<typename T>
T* func2(const T* szTarget)
{
...
}
class MyClass
{
public:
int func1(const char* szTarget);
int func1(const wchar_t* szTarget);
char* func2(const char* szTarget);
wchar_t* func2(const wchar_t* szTarget);
};
Actually you're defining two template function outside the scope of your class, they are not related with your class by any way.
So why not just :
class MyClass
{
public:
template<typename T>
int func1(const T* szTarget)
{
/* ... */
}
template<typename T>
T* func2(const T* szTarget)
{
/* ... */
}
};
By the way, you should experiment with scopes and naming to understand it a bit: http://ideone.com/65Mef5
What about
class MyClass {
public:
template<typename T>
int func1(T* szTarget) {
// provide appropriate implementation
}
template<typename T>
char* func2(T* szTarget) {
// provide appropriate implementation
}
};
The compiler is right.
You have to declare the template functions as members of the class. Which means they need to be declared inside the class declaration.
class MyClass
{
public:
template<typename T>
int func1(const T* szTarget)
{
...
}
template<typename T>
T* func2(const T* szTarget)
{
...
}
template <> int func1(const char* szTarget) { } //specialization
template <> int func1(const wchar_t* szTarget) { } //specialization
template <> char* func2(const char* szTarget) { } //specialization
template <> wchar_t*func2(const wchar_t* szTarget) { } //specialization
};
You haven't defined any templated methods in your class. One way of doing it is as follows:
class MyClass
{
public:
template <typename T> int func1(const T* szTarget);
template <typename T> T* func2(const T* szTarget);
};
template<typename T>
int MyClass::func1<T>(const T* szTarget)
{
...
}
template<typename T>
T* MyClass::func2<T>(const T* szTarget)
{
...
}
Related
I try to implement a template with an explicit template specification. The template and its implementation is shown bellow:
template <typename T>
class MyClass {
private:
T data;
size_t size;
public:
MyClass();
~MyClass();
uint32_t getSize();
T getData();
void setData(T value);
};
template <class T>
MyClass<T>::MyClass()
{
size = sizeof(T);
}
template <>
MyClass<std::string>::MyClass()
{
size = 0;
}
/* and so on */
Now I have an issue when my explicit declaration contains also a class template. Let say, i would create an explicit template specialization of a vector (containing any primitive type like int, char, float,...) and store the element site in the size variable.
template <??>
MyClass<std::vector<?>>::MyClass()
{
size = sizeof(?);
}
How could I do this?
You should specialize class, not methods:
#include <string>
#include <vector>
template <typename T>
class MyClass {
private:
T data;
size_t size;
public:
MyClass();
~MyClass();
uint32_t getSize();
T getData();
void setData(T value);
};
template <class T>
MyClass<T>::MyClass()
{
size = sizeof(T);
}
template <>
MyClass<std::string>::MyClass()
{
size = 0;
}
template<class T>
class MyClass<std::vector<T>>
{
MyClass();
T data;
size_t size;
};
template<class T>
MyClass<std::vector<T>>::MyClass()
{
size = sizeof(T);
}
For example, I have a class template:
template <typename T>
class base {
public:
void set(T data) { data_=data; }
private:
T data_;
};
And for a certain type I would like to add a function, but also have functions from the template class.
template <>
class base<int>{
public:
void set(int data) { data_=data; }
int get(){ return data_;} //function specific to int
private:
int data_;
}
How to do that without copying all members from the template class?
With inheritance:
template <typename T> struct extra {};
template <> struct extra<int> {
public:
int get() const;
};
template <typename T>
class base : public extra<T> {
friend class extra<T>;
public:
void set(T data) { data_=data; }
private:
T data_ = 0;
};
int extra<int>::get() const{ return static_cast<const base<int>*>(this)->data_;}
Demo
You can do this by using enable_if from type_traits to enable the get function only when the template parameter is int. One example is shown below.
#include <type_traits>
template <typename T>
class base {
public:
template <typename X=T,
std::enable_if_t< std::is_same<X,typename T>::value
&& std::is_same<X,int>::value, bool> = false>
int get() { return data_; }
void set(T data) { data_=data; }
private:
T data_;
};
How to reduce code duplication of a class that is template specialized?
I am trying to create a class (MyArray) that acts like std::vector but receives raw-pointer as parameter in some functions.
Here is a simplified version of it :-
template<class T> class MyArray{
T database[10];
public: T& get(int index){return database[index];}
void set(int index, T t){
database[index]=t;
}
};
template<class T> class MyArray<std::unique_ptr<T>>{
T* database[10];
public: T*& get(int index){return database[index];}
void set(int index, std::unique_ptr<T> t){
T* tmp=t.release();
database[index]=tmp;
}
};
Here is a test:-
class B{};
int main() {
MyArray<B> test1;
MyArray<B*> test2;
MyArray<std::unique_ptr<B>> test3;
test3.set(2,std::make_unique<B>()));
return 0;
}
Question: Please demonstrate an elegant way to reduce the above code duplication in MyArray.
A solution that I wished for may look like :-
template<class T> class MyArray{
using U = if(T=std::uniquePtr<X>){X*}else{T};
U database[10];
public: U& get(int index){return database[index];}
void set(int index, T t){
U u = convert(t);//<-- some special function
database[index]=u;
}
};
There might be some memory leak / corruption. For simplicity, please overlook it.
I just want an idea/rough guide. (no need to provide a full run-able code, but I don't mind)
In real life, there are 20+ function in MyArray and I wish to do the same refactoring for many classes.
Edit: I have (minor) edited some code and tag. Thank AndyG and Jarod42.
Maybe can you delegate the implementation details to a struct you provide to your class, and you specialize this struct, not MyArray:
template <typename T>
struct Policy {
using type = T;
static type convert(T t) { ... }
};
template <typename T>
struct Policy<std::unique_ptr<T>> {
using type = T*;
static type convert(T t) { ... }
};
template <typename T, typename P = Policy<T>>
class MyArray
{
using type = typename P::type;
void set(int index, T t) { type result = P::convert(t); }
};
You might think about using a common base class for the common functionality:
template<class T>
class Base{
protected:
T database[10];
public:
T& get(int index){return database[index];}
};
template<class T>
class MyArray : public Base<T>{
public:
void set(int index, T t){
this->database[index]=t;
}
};
template<class T>
class MyArray<std::unique_ptr<T>> : public Base<T*>
{
public:
void set(int index, std::unique_ptr<T>&& t){
T* tmp=t.release();
this->database[index]=tmp; //a little different
}
};
Demo
I am writing a generalized container using a class template, with a restriction (policy) that the items stored in the container should derive from a specific base class.
Here is the definition of the class template
// GenericContainer.hpp
// --------------------------------------
class ContainerItem
{
protected:
virtual ContainerItem& getInvalid() = 0;
public:
virtual ~ContainerItem();
bool isValid() const;
};
template<typename D, typename B>
class IsDerivedFrom
{
static void Constraints(D* p)
{
B* pb = p; // this line only works if 'D' inherits 'B'
pb = p; // suppress warnings about unused variables
}
protected:
void IsDerivedFrom2() { void(*p)(D*) = Constraints; }
};
// Force it to fail in the case where B is void
template<typename D>
class IsDerivedFrom<D, void>
{
void IsDerivedFrom2() { char* p = (int*)0; /* error */ }
};
template <class T>
class GenericContainer : public IsDerivedFrom<T, ContainerItem>
{
private:
typedef std::vector<T> TypeVect;
void addElement(const T& elem);
TypeVect m_elems;
public:
unsigned int size() const;
T& elementAt(const unsigned int pos);
const T& elementAt(const unsigned int pos) const;
};
template <class T>
void GenericContainer<T>::addElement(const T& elem)
{
m_elems.push_back(elem);
}
template <class T>
unsigned int GenericContainer<T>::size() const
{
return m_elems.size();
}
template <class T>
T& GenericContainer<T>::elementAt(const unsigned int pos)
{
unsigned int maxpos = m_elems.size();
if (pos < maxpos)
return m_elems[pos];
return T::getInvalid();
}
template <class T>
const T& GenericContainer<T>::elementAt(const unsigned int pos) const
{
unsigned int maxpos = m_elems.size();
if (pos < maxpos)
return m_elems[pos];
return T::getInvalid();
}
// Class to be contained (PURPOSELY, does not derive from ContainerItem)
// Data.hpp
//----------------------------------------------------------------
class Data
{ /* implem details */};
// Container for Data items
// Dataset.h
// ----------------------------------------------------------------------------
#include "GenericContainer.hpp"
#include "Data.hpp"
class Dataset: public GenericContainer<Data>
{
public:
Data& getInvalid();
};
// C++ source
// -----------------------------------------------------------
#include "Dataset.hpp"
Dataset ds;
Can anyone explain why the code above compiles?.
[Edit]
The code above should NOT compile for two reasons:
The class 'Data' does NOT derive from ContainerItem, and yet it can be stored in GenericContainer (as illustrated by the class Dataset). Incidentally, this issue has now been resolved thanks to the answer given by Omifarious and jdv
The class 'Data' does NOT implement the pure virtual method declared in the ABC ContainerItem - using the fixes recommended in the answers below, the first issue (enforcement of policy) is resolved, however the compiler fails to notice that Data does not implement the getInvalid() method of the ContainerItem 'interface'. Why is the compiler missing this glaring mistake?
BTW, compiler and OS details are:
g++ (Ubuntu 4.4.3-4ubuntu5) 4.4.3
Change IsDerivedFrom2 to IsDerivedFrom and it fails to compile in just the expected manner.
The problem is that a method from a template class is never instantiated if it isn't called. Changing the name makes it a constructor, so it then ends up being called by the constructors of classes derived from IsDerivedFrom. It will still compile to empty code. The compiler will optimize it away the dead assignment.
I would recommend you not write template code like this yourself if you can manage to use Boost, particularly is_base_of from the Boost type traits library.
In particular, your GenericContainer template can be more simply and easily implemented this way using Boost:
#include <boost/static_assert.hpp>
#include <boost/type_traits/is_base_of.hpp>
template <class T>
class GenericContainer
{
private:
typedef std::vector<T> TypeVect;
void addElement(const T& elem);
TypeVect m_elems;
public:
unsigned int size() const;
T& elementAt(const unsigned int pos);
const T& elementAt(const unsigned int pos) const;
GenericContainer() {
BOOST_STATIC_ASSERT( (::boost::is_base_of<ContainerItem, T>::value) );
}
};
template <class T>
void GenericContainer<T>::addElement(const T& elem)
{
m_elems.push_back(elem);
}
template <class T>
unsigned int GenericContainer<T>::size() const
{
return m_elems.size();
}
template <class T>
T& GenericContainer<T>::elementAt(const unsigned int pos)
{
unsigned int maxpos = m_elems.size();
if (pos < maxpos)
return m_elems[pos];
return T::getInvalid();
}
template <class T>
const T& GenericContainer<T>::elementAt(const unsigned int pos) const
{
unsigned int maxpos = m_elems.size();
if (pos < maxpos)
return m_elems[pos];
return T::getInvalid();
}
The Constraints function is not generated because IsDerivedFrom2 is never referenced. This is required behavior for C++. Maybe it helps to call it from the constructor. Otherwise, check the boost library for functionality like this.
Why am I getting this error:
Error 1 error C2662: 'Allocator::Allocate' : cannot convert 'this' pointer from 'const Allocator' to 'Allocator &' ?
Thats code:
/*Allocator.h*/
/*Not finished yet but working*/
#pragma once
template<class T>
class Allocator
{
public:
//typedef T value_type;
typedef T* pointer;
pointer Allocate(std::size_t count);
pointer Construct(void* address, const pointer obj);
template<class FwdIter>
void Destroy_(FwdIter first,FwdIter last);
void Deallocate_(void* where);
Allocator();
~Allocator();
private:
void Destroy_(const T* obj);
};
/*Allocator_impl.hpp*/
#pragma once
#include "StdAfx.h"
#include "Allocator.h"
template<class T>
Allocator<T>::Allocator()
{
}
template<class T>
Allocator<T>::~Allocator()
{
/*Destroy();
Deallocate();*/
}
template<class T>
typename Allocator<T>::pointer Allocator<T>::Allocate(std::size_t count)
{
return static_cast<pointer>(::operator new(sizeof(value_type) * count));
}
template<class T>
typename Allocator<T>::pointer Allocator<T>::Construct(void* address, const pointer obj)
{
return new (address) T(*obj);
}
//template<class T>
//void Allocator<T>::Destroy()
//{
// //Destroy_(addressBegin_, addressBegin_ + size_);
//}
template<class T>
void Allocator<T>::Destroy_(const T* obj)
{
obj->~T();
}
template<class T>
template<class FwdIter>
void Allocator<T>::Destroy_(FwdIter first,FwdIter last)
{
while (first != last)
{
Destroy_(&*first);
++first;
}
}
template<class T>
void Allocator<T>::Deallocate_(void* address)
{
::operator delete(address);
}
//template<class T>
//void Allocator<T>::Deallocate()
//{
// //Deallocate_(addressBegin_);
//}
/*Toy.h*/
#pragma once
#include "Allocator_impl.hpp"
/*As a base to managed memory*/
template<class T, class A = Allocator<T>>
class ToyBase
{
typedef T* pointer;
private:
A alloc_;
protected:
//--------------------------------------COMMENT HERE
pointer Allocate(const std::size_t)const;<------------When invoking this fnc from
explicit ToyBase();
virtual ~ToyBase();
};
template<class T, class A>
ToyBase<T,A>::ToyBase()
{}
template<class T, class A>
ToyBase<T,A>::~ToyBase()
{}
//--------------------------------------AND COMMENT HERE
template<class T, class A>
typename ToyBase<T,A>::pointer ToyBase<T,A>::Allocate(const std::size_t count)const
{
return alloc_.Allocate(count);<-----------here
}
/*
But when I remove const from fnc decl. it works. I do not understand it as I do not change an object merely invoke fnc on its member.
*/
template<class T>
class ToyRepresentation : private ToyBase<T>
{
public:
typedef T value_type;
typedef T* pointer;
ToyRepresentation(const std::size_t = 0);
void Push(T*);
void Pop();
void GetSize()const;
void GetCapacity()const;
void SetCapacity(const std::size_t);
void Reset();
private:
pointer data_;
std::size_t size_;
std::size_t capacity_;
static unsigned TOTAL_; //total number of created objects
};
template<class T>
unsigned ToyRepresentation<T>::TOTAL_ = 0;
template<class T>
ToyRepresentation<T>::ToyRepresentation(const std::size_t count = 0): ToyBase<T>(), data_(Allocate(count)), size_(0), capacity_(count)
{
}
/*tmain*/
#include "stdafx.h"
#include "Toy.h"
int _tmain(int argc, _TCHAR* argv[])
{
try
{
ToyRepresentation<int> t;
}
catch(const std::exception&)
{
}
return 0;
}
Comments to interesting lines are marked in code. Thanks.
alloc_.Allocate is not a const method. You can "fix" (or hide) this warning by making alloc_ mutable, although this should not be done without understanding of why the compiler is warning you.
Not sure why the method that calls this needs to be const anyhow. Allocation is not something that typically is expected to leave its context unchanged.
ToyBase<T,A>::Allocate is const qualified which means that you cannot invoke any non-const methods on any members of this as they too are const-qualified now.
Try FAQ 18.10.