C++: initializing template constructor/routine declared in header file? - c++

I have a template defined in my header file as follows:
template<typename T> class BoundedBuffer {
unsigned int size;
T entries[];
public:
BoundedBuffer( const unsigned int size = 10 );
void insert( T elem );
T remove();
};
However, when I try to initialize the constructor:
BoundedBuffer<T>::BoundedBuffer( const unsigned int size = 10 ) size(size) {
// create array of entries
entries = new T[size];
// initialize all entries to null
for(int i = 0; i < size; i++)
entries[i] = null;
}
I get the following error (the first line of the previous code block is 17):
q1buffer.cc:17: error: âTâ was not declared in this scope
q1buffer.cc:17: error: template argument 1 is invalid
q1buffer.cc:17: error: expected initializer before âsizeâ

The right syntax is:
template <typename T>
BoundedBuffer<T>::BoundedBuffer(const unsigned int size) : size(size) {
// create array of entries
entries = new T[size];
// initialize all entries to null
for(int i = 0; i < size; i++)
entries[i] = null;
}
Note that optional parameters should not be declared in functions definitions but ony in functions declarations.
class aaa
{
// declaration
void xxx(int w = 10);
};
// definition
void aaa::xxx(int w)
{
...
}
Note that everything for templated class should stay in H files.
"They must be in the same translation unit. It is quite common in some libraries to separate the template implementation into a .tpp (or some other extension) file that is then included in the .h where the template is declared." as Michael Price said.
Templates are not normal types and they cannot be linked.
They are instantiated only when requested.
Note that constructors fields initializers need the ":" character.
class MyClass
{
public:
int x;
MyClass() : x(10) { /* note that i used : character */ }
};

You have to implement all methods of the template in the header, because users of the template need to be able see those methods to instantiate it for a given type.

Your declaration should be:
template< typename T >
BoundedBuffer<T>::BoundedBuffer( const unsigned int size ) : size( size ) {...}
Note that it also has to be in the header file, as mentioned by #Dean Povey.

Related

Can I initialize a `constexpr static` member outside the class?

I'm working with a variable-width communications format. The structs to handle it look something like this:
struct Header
{
int msgType = -1, len;
Header() { len = sizeof(*this); }
};
struct A : public Header
{
int x; char y;
A() { msgType = 1; len = sizeof(*this); }
};
// Further structs B, C, ... declared along the same lines
I would like to have a constexpr static member Header::MAX_SIZE which gives the max size of any of these derived classes, e.g. so I can allocate a buffer which is guaranteed to hold any such packet. So I'd like to do something like
struct Header
{
int msgType = -1, len;
constexpr static std::size_t MAX_SIZE;
Header() { len = sizeof(*this); }
};
// ... declaration of subclasses ...
inline Header::MAX_SIZE = std::max({ sizeof(A), sizeof(B), sizeof(C) });
I need the definition to come outside of the class because it depends on sizeof(A), etc., which in turn depend on the definition of Header.
It seems like this sort of thing should be unobjectionable: I'm giving the definition of the member in the same source file, and it can be computed at compile time. But I haven't found any way to tell the compiler to actually do this.
constexpr goes on the initializing declaration of a variable, so just put it outside the class:
struct Header
{
int msgType = -1, len;
static const std::size_t MAX_SIZE;
Header() { len = sizeof(*this); }
};
// ... declaration of subclasses ...
inline constexpr std::size_t Header::MAX_SIZE = std::max({ sizeof(A), sizeof(B), sizeof(C) });
Note that the implicit const must be spelled out in the declaration. The definition should go in the same header to avoid any translation unit seeing the declaration but not the inline, which is not allowed.

i can't understand this, can someone help me out with the explanation? [duplicate]

This question already has answers here:
Why can templates only be implemented in the header file?
(17 answers)
Closed 3 years ago.
The following is a Template classes tutorial from learncpp.com :
main.cpp
#include "Array.h"
int main()
{
Array<int> intArray(12);
Array<double> doubleArray(12);
for (int count = 0; count < intArray.getLength(); ++count)
{
intArray[count] = count;
doubleArray[count] = count + 0.5;
}
for (int count = intArray.getLength()-1; count >= 0; --count)
std::cout << intArray[count] << "\t" << doubleArray[count] << '\n';
return 0;
}
Array.h
#ifndef ARRAY_H
#define ARRAY_H
#include <assert.h> // for assert()
template <class T>
class Array
{
private:
int m_length;
T *m_data;
public:
Array()
{
m_length = 0;
m_data = nullptr;
}
Array(int length)
{
m_data = new T[length];
m_length = length;
}
~Array()
{
delete[] m_data;
}
void Erase()
{
delete[] m_data;
// We need to make sure we set m_data to 0 here, otherwise it will
// be left pointing at deallocated memory!
m_data = nullptr;
m_length = 0;
}
T& operator[](int index)
{
assert(index >= 0 && index < m_length);
return m_data[index];
}
// The length of the array is always an integer
// It does not depend on the data type of the array
int getLength();
};
#endif
Array.cpp
#include "Array.h"
template <typename T>
int Array<T>::getLength() { return m_length; }
Error : unresolved external symbol "public: int __thiscall Array::getLength(void)" (?GetLength#?$Array#H##QAEHXZ)
Explanation : In order for the compiler to use a template, it must see both the template definition (not just a declaration) and the template type used to instantiate the template. Also remember that C++ compiles files individually. When the Array.h header is #included in main, the template class definition is copied into main.cpp. When the compiler sees that we need two template instances, Array, and Array, it will instantiate these, and compile them as part of main.cpp. However, when it gets around to compiling Array.cpp separately, it will have forgotten that we need an Array and Array, so that template function is never instantiated. Thus, we get a linker error, because the compiler can’t find a definition for Array::getLength() or Array::getLength().
What does the explanation mean? i am having a hard time understanding the explanation provided by Alex(learncpp's creator).
// The length of the array is always an integer
// It does not depend on the data type of the array
int getLength();
It's true that the length of the array does not depend on the data type. However, Array<double>::getLength() and Array<int>::getLength() are two different functions. You can implement getLength() for all template instantiations only if it is implented inline. For that reason, it's not possible to implement it in a .cpp file.

Template class copy constructor

I want to write copy constructor for a template class. I have this class:
template<int C>
class Word {
array<int, C> bitCells; //init with zeros
int size;
public:
//constructor fill with zeros
Word<C>() {
//bitCells = new array<int,C>;
for (int i = 0; i < C; i++) {
bitCells[i] = 0;
}
size = C;
}
Word<C>(const Word<C>& copyObg) {
size=copyObg.getSize();
bitCells=copyObg.bitCells;
}
}
I have errors with the copy constructor, on the line of intilizeing the size, I get:
"Multiple markers at this line
- passing 'const Word<16>' as 'this' argument of 'int Word::getSize() [with int C = 16]' discards qualifiers [-
fpermissive]
- Invalid arguments ' Candidates are: int getSize() '"
what is wrong with this ?
thank you
I'd write the class like this:
template <std::size_t N>
class Word
{
std::array<int, N> bit_cells_;
public:
static constexpr std::size_t size = N;
Word() : bit_cells_{} {}
// public functions
};
Note:
No need for a dynamic size, since it's part of the type.
No need for special member functions, since the implicitly defined ones are fine.
Initialize the member array to zero via the constructor-initializer-list.
Template parameter is unsigned, since it represents a count.
What's wrong is that your getSize() is not declared const. Make it so:
int getSize() const { return size; }

pointer initialization with template typename

I've got class that inherite from template class. I would like to initialize pointer with template argument. How can I do that?
Algorithm.h:
#ifndef ALGORITHM_H
#define ALGORITHM_H
#include <iostream>
using namespace std;
template <typename T>
class Algorithm
{
protected:
T data;
T result; //(*)
int dataSize;
int resultSize;
public:
Algorithm(){}
Algorithm(T in, int inSize){
cout<<"Algorithm constructor!"<<endl;
data = in;
dataSize = inSize;
resultSize = dataSize;
result = new T; //(**)
for (int i = 0; i<this->resultSize; i++){
this->result[i] = 0;
cout<<"i: "<<i<<" *(this->result+i) = "<<this->result[i]<<endl;
}
}
#endif // ALGORITHM_H
Error is in (**) line:
/home/user/Projects/Algorithms/algorithm.h:23: error: cannot
convert 'float**' to 'float*' in assignment
result = new T;
^
I could change line (*) but it is not my favourite solution as it will be inconsistent with data - I would rather that to be so. So how can I initialize it to feel all result table with 0s then?
If you don't want to change the (*) line to T* result, then you can use std::remove_pointer<> type trait (C++11 or later)
result = new typename std::remove_pointer<T>::type(); // a single element value-initialized
or (if you want an array, which is probably what you want)
result = new typename std::remove_pointer<T>::type [resultSize]; // array of resultSize elements
Finally, you can even value-initialize your array as
result = new typename std::remove_pointer<T>::type [resultSize]{}; // value-initialized array
However I find this solution awkward (to say the least), and it is probably much more clear if you use T* result instead.

error: Illegal zero sized array

I get this error:
error C2229: class 'GenerateRandNum<int [],int>' has an illegal zero-sized array
In my main, I call my random generator function to input into a empty data set
I call the method in my main like so:
//declare small array
const int smallSize = 20;
int smallArray[smallSize];
// call helper function to put random data in small array
GenerateRandNum <int[], int> genData(smallArray, smallSize);
genData.generate();
Header file
template <class T, class B>
class GenerateRandNum
{
public:
T data;
B size;
GenerateRandNum(T list, B length)
{
data = list;
size = length;
}
void generate();
};
File with method definition
template<class T, class B>
void GenerateRandNum<T, B> ::generate()
{
for (B i = 0; i < size; i++)
{
data[0] = 1 + rand() % size;
}
}
Pointers and arrays are not the same in C/C++. They are two very different things. However, arrays decay into pointers. Most notably in function declarations: The declaration
void foo(int array[7]);
is defined to be equivalent to
void foo(int* array);
That said, all the GenerateRandNum constructor gets, is a int* because that's what T = int [] decays to in the function declaration context. The data member of GenerateRandNum, however, is of type int [] (no decay here), which your compiler assumes to be a zero sized array. Consequently, when you try to assign a pointer to the array, your compiler complains.
You have two options to fix this:
You use an std::vector<> instead, as Marco A. suggests.
You declare your GenerateRandNum class as:
template <class T>
class GenerateRandNum {
public:
T* data;
size_t size;
GenerateRandNum(T* list, size_t length) {
data = list;
size = length;
}
void generate();
};
Note:
I have removed the template parameter for the size type: size_t is guaranteed to be suitable for counting anything in memory, so there is absolutely no point in using anything different. Templating this parameter only obfuscates your code.
There are some problems with your approach:
The first array template parameter can't have its dimension deduced from the argument as n.m. noted, you would need to specify it explicitly:
GenerateRandNum<int[20], int>
There no point in doing
data = list
since in your code sample these are two arrays and you can't assign them directly. You can either copy the memory or specialize your routines/template
You should really consider using a vector of integers, e.g.
template <class T, class B>
class GenerateRandNum
{
public:
T data;
B size;
GenerateRandNum(T list, B length) {
data = list;
size = length;
}
void generate();
};
template<class T, class B>
void GenerateRandNum<T, B> ::generate()
{
srand((unsigned int)time(NULL)); // You should initialize with a seed
for (B i = 0; i < size; i++) {
data[i] = 1 + rand() % size; // I believe you wanted data[i] and not data[0]
}
}
int main(){
//declare small array
const int smallSize = 20;
std::vector<int> smallArray(smallSize);
// call helper function to put random data in small array
GenerateRandNum <std::vector<int>, int> genData(smallArray, smallSize);
genData.generate();
}
Example
I fixed two issues in the code above, take a look at the comments.