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.
Related
template <typename StoredT>
class InternalObject {
public:
using RefCountT = unsigned short;
template <typename... CtorTs>
static void* emplace(CtorTs&&...);
Types type;
RefCountT reference_count;
bool is_immovable;
StoredT stored_value;
InternalObject();
InternalObject(StoredT);
~InternalObject();
};
My class has a member StoredT stored_value which I would like to be able to construct using emplace and return a void* to it.
However, if I want to do this, I would have to do InternalObject<StoredT> *io_ptr = new InternalObject<StoredT>; which would force me to default-construct stored_value.
The solution I attempted was to allocate the appropriate amount of space as an array of unsigned char (the returned pointer is a heap pointer). Then, I tried to increment the pointer by appropriate amounts and modify the value there.
A more reproducible & complete example which does not produce a valid (non-POD) value for two.
#include <iostream>
#include <vector>
struct S {
int one;
std::vector<int> two;
};
int main() {
unsigned char *s_ptr = new unsigned char[sizeof(S)];
S *s = reinterpret_cast<S*>(s_ptr);
*s_ptr = 100; // Fine
std::vector<int> *vec_ptr = reinterpret_cast<std::vector<int>*>(s_ptr + sizeof(int));
*vec_ptr = {5,6,7};
std::cout << s->two.capacity() << "\n"; // big ol' number
return 0;
}
Consider using std::optional<StoredT>, which will allow you to defer the construction of the StoredT that you want to hold:
#include <optional>
template <typename StoredT>
class InternalObject {
public:
using RefCountT = unsigned short;
template <typename... CtorTs>
void emplace(CtorTs&&... args) {
stored_value.emplace(args...);
}
Types type;
RefCountT reference_count;
bool is_immovable;
std::optional<StoredT> stored_value;
InternalObject();
InternalObject(StoredT);
~InternalObject();
};
I'm attempting to implement an overloaded casting operator on my templated array2d class using type T. So I'm to cast from array2d<T> to a new array2d<E>.
I'm able to perform the casting itself but problems arise when I try to set the casted data to the new instance of array2d<E>. The compiler tells me that the casting operator doesn't have access to the private members of array2d
Here's where I am so far (edited out unrelated code for brevity)
array2d.h
template<typename T>
class array2d {
private:
// Member Variables
T** data;
size_t width, height;
public:
// constructors, methods, etc...
// Cast Operator
template<typename E>
operator array2d<E>() const;
};
// Other overloaded operators...
// Overloaded Casting Operator
template<typename T>
template<typename E>
array2d<T>::operator array2d<E>() const{
// Create new instance
array2d<E> castedArr(width, height);
// Allocate memory for the casted data, then cast each element
E** newData = new E*[castedArr.get_height()];
for (size_t i = 0; i < castedArr.get_height(); i++){
newData[i] = new E[castedArr.get_width()];
for (size_t j = 0; j < castedArr.get_width(); j++){
newData[i][j] = (E)data[i][j];
}
}
// issue here, can't set data because it's private.
castedArr.data = newData;
delete [] newData;
newData = nullptr;
return castedArr;
}
main.cpp
#include "array2d.h"
int main(int argc, char *argv[]) {
// Cast Operator
// Create an array2d<T> of
// width = 5
// height = 5
// fill all elements with 42.1
array2d<double> x(5, 5, 42.1);
// Create a new array exactly the same as
// x, where x is casted to int
array2d<int> y = (array2d<int>) x;
return 0;
}
This confused me as I have many other overloaded operators that can access the private members just fine using practically the exact same logic.
Why does this happen and what can I do to rectify it?
When writing a template, you don't nail down the actual type, you create a blue print for different types. array2d<double> and array2d<int> are different types, and by default, two instances of two different classes can't access their private members.
You can fix that by declaring every instantiation of array2d a friend class of the template array2d:
template<typename T>
class array2d {
/* ... */
template<class E> friend class array2d;
/* ... */
};
As a side note, I'm not quite sure wheter
delete [] newData;
is a good idea. You're destroying parts of the resources that the new array2d instance is supposed to manage. If you delete[] that again in array2d::~array2d(), you'll have undefined behavior.
#include <iostream>
#include <string>
using namespace std;
template <class T> int IsSubArr(T& a, int a_len, T& b, int b_len)
{
int i,j;
bool found;
int k;
T& s=a,l=b;
int s_len = (a_len < b_len) ? a_len : b_len; // find the small array length
if (s_len == a_len) // check to set pointers to small and long array
{
s = a;
l = b;
}
else
{
s = b;
l = a;
}
for (i = 0; i <= a_len-s_len; i++) //loop on long array
{
found = true;
k=i;
for (j=0; j<s_len; j++) // loop on sub array
{
if (s[j] != l[i])
{
found = false;
break;
}
k++;
}
}
if (found)
return i;
else
return -1;
}
/******* main program to test templates ****/
int main()
{
int array[5] = {9,4,6,2,1};
int alen = 5;
int sub_arr[3] = {6,2,1};
int slen = 3;
int index= 0;
index = IsSubArr(array,alen,sub_arr,slen);
cout << "\n\n Place of sub array in long array: " << index;
cout << endl;
return 0;
}
for this line of code:
index = IsSubArr(array,alen,sub_arr,slen);
i get error:
Error 1 error C2782: 'int IsSubArr(T &,int,T &,int)' : template parameter 'T' is ambiguous
please help to resolve this issue ?
Since array[a] and array[b] where a != b are 2 different types, you'll need 2 type templates args.
A work around would be to use pointers.
+ template <class T> int IsSubArr(T* a, int a_len, T* b, int b_len)
+ T* s = a; T*l = b;
You defined the first and the third parameters as references
template <class T> int IsSubArr(T& a, int a_len, T& b, int b_len)
^^^^ ^^^^
and pass as arguments for these parameters two arrays with different types
int array[5] = {9,4,6,2,1};
int sub_arr[3] = {6,2,1};
//...
index = IsSubArr(array,alen,sub_arr,slen);
^^^^^ ^^^^^^^
The first argument has type int[5] and the third argument has type int[3]
So the compiler is unable to deduce the referenced type T.
If you are going to use arrays with the function then you could declare it like
template <class T, size_t N1, size_t N2>
int IsSubArr( T ( &a )[N1], T ( &b )[N2] );
Or you could use pointers instead of the references to arrays
template <class T> int IsSubArr( T *a, size_t a_len, T *b, size_t b_len );
Take into account that this declaration within the function
T& s=a,l=b;
is also wrong. It is equivalent to the following declarations
T& s=a;
T l=b;
That is the first declaration declares a reference to an array while the second declaration declares an array and tries to initialize it with another array. However arrays do not have a copy constructor and the compiler will issue one more error. And you may not reassign a reference.
You should know that there is standard algorithm std::search declared in header <algorithm> that can do the job you want to do with your function.
It's because array and sub_arr are two different types. array is of type int[5] while sub_arr is of type int[3]. The array dimensions are part of the type.
Either change the function to use two different templates arguments, one for each array, or use pointer (e.g. T*)
There's also another error, that you will continue to have if you keep using arrays and two different template arguments, and that is that you can't change references. Once you have assigned to the variable s and l in the function, those can't be made to reference anything else. The latter assignments of the s and l variables will fail because there you try to assign the arrays to each other, which you can not do, you can only copy arrays.
If you use pointers instead, then this won't be a problem.
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.
I am creating a generic data structure and I want to return a vector that contains some of the objects in my structure.
I tried
template<class T>
vector<T> DataStructure<T>::getItems(int count)
{
vector<T> items;
for(int i = 0; i < count; i++)
items.push_back(data[i]);
return items;
}
But the compiler says
error: ISO C++ forbids declaration of 'vector' with no type
error: expected ';' before '<' token
vector is not defined.
You need to #include <vector> and to specify its namespace either using std::vector or putting an using namespace std; in your function or at the global scope (this latter suggestion should be avoided).
#include <vector>
template<class T>
std::vector<T> DataStructure<T>::getItems(int count)
{
std::vector<T> items;
for(int i = 0; i < count; i++)
items.push_back(data[i]);
return items;
}
It's std::vector, not just vector. Other than that,data is undefined in the snippet. But in general, this is the way to return a vector.
As an complement to #etarion perfect answer, the most idiomatic way to perform your operation is, assuming data is of type T*:
template<class T>
std::vector<T> DataStructure<T>::getItems(int count)
{
return std::vector<T>(data, data + count);
}
Since getItems' definition must be available through the header anyway, as it is a method of a class template, it is easiest to define it within the class definition:
template<class T>
struct DataStructure {
std::vector<T> getItems(int count) const {
assert(0 <= count && count <= data.size()); // don't forget to check count
// if you must use op[] with data:
// std::vector<T> items;
// for(int i = 0; i < count; i++)
// items.push_back(data[i]);
// return items;
// if data is a container (using random-access iterators here):
return std::vector<T>(data.begin(), data.begin() + count);
// if data is an array:
// return std::vector<T>(data, data + count);
}
std::vector<T> data; // or is data something else?
};