I'm trying to make a simple template of creating an array class. However, I want to use two arguments: one typename and one int variable. I'm running into two errors and I'm not sure what I'm doing wrong. Here is my source which is simple and works.
int main()
{
Array<int, 7> tArray;
std::cout << tArray.getSize() << std::endl;
for (int i = 0; i < tArray.getSize(); i++)
{
tArray.insert(i + 5);
i++;
}
tArray.printArray();
return 0;
}
The error/issue I'm having is regarding two functions in my header file.
#pragma once
template <typename T, int S>
class Array
{
private:
T arr[S];
public:
Array();
int getSize() const;
int insert(T val);
void printArray();
~Array();
};
cArray<T, S>::cArray() { }
template <typename T, int S>
int Array<T, S>::insert(T val)
{
Array<T, S>* temp = new Array[S];
temp = this;
return temp[val];
}
template <typename T, int S>
void Array<T, S>::printArray()
{
Array<T, S>* temp = new Array[S];
temp = this;
for (int i = 0; i < S; i++)
{
std::cout << tempArray[i] << std::endl;
}
}
The functions insert and printArray are not working. For the insert function, the error being returned states: Suppression State Error C2440 'return': cannot convert from 'Array<int,7>' to 'int'
The printArray is displaying nothing.
This is the first time I'm using a template with two arguments so I'm a bit confused on what I'm doing wrong here. Any takes?
The offending code is here:
template <typename T, int S>
int Array<T, S>::insert(T val) {
Array<T, S>* temp = new Array[S];
temp = this;
return temp[val];
}
First, you create an array of Arrays, because you wrote an expression that looks like:
Type *var = new Type[count];
Then you change the pointer to that array to this. Then you try to return the element at index val of the array temp, and this element has type Array<T, S>. But, the return type is int, so, that is why the compiler complains that it cannot convert Array<int, 7> to int.
You should not use new here at all. You already have allocated memory for the array (it's the member variable arr), you just need to copy val into the right place in that array. To do that, you need to keep track of how many elements you already inserted in the array. (Note that S is a constant that just says what the maximum amount of elements is you can store.) So:
template <typename T, int S>
class Array {
T arr[S];
size_t size = 0;
...
};
template <typename T, int S>
void Array<T, S>::insert(T val)
{
arr[size++] = val;
}
template <typename T, int S>
size_t Array<T, S>::getSize()
{
return size;
}
And printArray() has similar issues. You should print the contents of arr, you should not create a new temporary array and try to print its elements.
Related
This question already has answers here:
How to avoid decay with template parameter deduction
(2 answers)
Closed 3 years ago.
I have this code:
template <typename T, ::std::size_t size>
using ary_t = T[size];
template <typename T, ::std::size_t size>
constexpr int call_me(ary_t<T const, size> &a)
{
int total = 10;
for (::std::size_t i = 0; i < size; ++i) {
total += a[i];
}
return total;
}
template <typename T>
constexpr int call_me(T const *a)
{
int total = 0;
for (int i = 0; a[i]; ++i) {
total += a[i];
}
return total;
}
#if 0
int t1()
{
return call_me("a test");
}
#endif
int t2()
{
char const * const s = "a test";
return call_me(s);
}
and it works, but when remove the #if 0 section around t1 it fails to compile because of an ambiguity in which template to use. Is there any way to force the array version of call_me to be used preferentially?
I've tried a number of different tricks to make this work. I've tried adding , int... to the template argument list for the pointer version. I've tried removing the const. I've tried both. I've even tried making the pointer version into a C-style varargs function (aka int call_me(T const *a, ...)). Nothing seems to work.
I'd be happy with an answer that requires what is currently believed will make it into C++2a.
There's an easy workaround:
template <typename T>
constexpr int call_me(T&& arg) {
if constexpr(std::is_pointer_v<std::remove_reference_t<T>>) {
return call_me_pointer(arg);
} else {
return call_me_array(arg);
}
}
If you accept to add a level of indirection, you can add an unused parameter to give the precedence to the array version.
I mean
template <typename T, std::size_t size>
constexpr int call_me_helper (ary_t<T, size> &a, int)
{
int total = 10;
for (std::size_t i = 0; i < size; ++i) {
total += a[i];
}
return total;
}
template <typename T>
constexpr int call_me_helper (T const * a, long)
{
int total = 0;
for (int i = 0; a[i]; ++i) {
total += a[i];
}
return total;
}
template <typename T>
constexpr int call_me (T const & a)
{ return call_me_helper(a, 0); }
I suggest you achieve the same effect by using a span:
What is a "span" and when should I use one?
You just replace the array reference with a fixed-size span:
#include <cstddef>
#include <gsl/span>
template <typename T, std::size_t size>
constexpr int call_me(gsl::span<T const, size> a)
{
int total = 10;
for (std::size_t i = 0; i < size; ++i) {
total += a[i];
}
return total;
}
And there's no ambiguity. What's even nicer is that now you can use standard-library algorithms on containers:
#include <numeric>
template <typename T, std::size_t size>
constexpr int call_me(gsl::span<T const, size> a)
{
return std::accumulate(a.begin(), a.end(), 10);
}
For a school assignment I want to build a custom array container as I'm not allowed to use containers provided by std, or any library available, while self made is allowed.
So far everything I have is working but I want to double my array size as soon as I reach the limit. how can i do this, all i can find is using vector (which i'm not allowed).
#ifndef UTILS_ARRAY_HEADER_INCLUDED
#define UTILS_ARRAY_HEADER_INCLUDED
#include <array>
namespace utils
{
template <class T>
struct Array
{
private:
int count = 0;
int size = 1;
std::array<T, 1> myArray;
void doubleSize();
public:
T* begin();
T* end();
T& operator[] (int);
void addItem(T const);
};
template <class T>
T* Array<T>::begin()
{
return &myArray[0];
}
template <class T>
T* Array<T>::end()
{
if (&myArray[count])
return &myArray[count];
return &myArray[0];
}
template <class T>
T& Array<T>::operator[] (int key)
{
return myArray[key];
}
template <class T>
void Array<T>::addItem(T const item)
{
if (count >= 0 && count < size)
{
myArray[count] = item;
count++;
}
else {
doubleSize();
}
return;
}
template <class T>
void Array<T>::doubleSize()
{
// ?
/*size = size * 2;
const int newsize = 2;
std::array<T, newsize> newArray; // not working.
std::copy(std::begin(myArray), std::end(myArray), std::begin(newArray));
myArray = newArray;*/
}
}
#endif
You need properties:
current capacity: int
current size (max used index): int
pointer to your data: T *
In AddItem check if current_size < current_capacity. If yes,
create new_data with size of currernt_capacity * 2, copy each item, delete old data and replace pointer.
Remember to do delete data; in destructor. I won't give you more code, it's your homework.
Checkout valgrind to check if your code does not leak memory.
I suspect that std::array<T, 1> is not allowed either. But that's not a real problem: you can't use array anyway since it has a fixed size.
You'll need to store a T* begin, a size_t current_size and a size_t size_in_use.
I would like to write a class template that is used to contain sequences of elements. I am working on a method that will behave like push_back() in vector. This is what i have done so far:
template <class T, int N>
class mysequence{
T memblock[N];
public:
void setmember(T value);
T getmember(int x);
};
template <class T, int N>
void mysequence<T, N>::setmember(T value) {
if (sizeof(memblock) == 1) memblock[0] = value;
else {
int y = sizeof(memblock) / sizeof(memblock[0]);
memblock[y] = value;
}
}
template <class T, int N>
T mysequence<T, N>::getmember(int x) {
return memblock[x];
}
int main()
{
mysequence < int, 14 > myints;
myints.setmember(9);
cout << myints.getmember(0);
}
And it returns:
-858993460
with error message. As far as i know the sizeof an empty class or structure is 1. But i also tried:
if (sizeof(memblock) == NULL) memblock[0] = value;
But all the same. I can not figure out what`s wrong with my code. If anyone has any ideas, i will apressiate it.
What is wrong is your use of sizeof. It does not tell you how many elements do you have, but just the memory used (N * sizeof(T)). To keep track of elements contained you seed a separate counter.
You are asking getmember() to return the first element of the array, but setmember() never fills in that element unless T is char, unsigned char, or bool, and N is 1. In any other combination, setmember() is storing the value past the end of the array. If you want to store a value in the last element, use N-1 instead (no need to calculate y as it will match N):
template <class T, int N>
void mysequence<T, N>::setmember(T value) {
memblock[N-1] = value;
}
But, that is no use in a multi-element array. Since you want to mimic push_back(), you need to have setmember() store the value in the last available element instead:
template <class T, int N>
class mysequence{
T memblock[N];
int count;
public:
mysequence() : count(0) {}
void setmember(T value);
T getmember(int x);
};
template <class T, int N>
void mysequence<T, N>::setmember(T value) {
if (count < N) {
memblock[count-1] = value;
++count;
}
else
throw length_error("sequence is full");
}
template <class T, int N>
T mysequence<T, N>::getmember(int x) {
if ((x < 0) || (x >= count))
throw out_of_range("x is out of range");
return memblock[x];
}
int main()
{
mysequence < int, 14 > myints;
myints.setmember(9);
cout << myints.getmember(0);
}
Or, you could simply get rid of your array and use a real vector instead:
template <class T>
class mysequence{
vector<T> memblock;
public:
void setmember(T value);
T getmember(int x);
};
template <class T>
void mysequence<T>::setmember(T value) {
memblock.push_back(value);
}
template <class T>
T mysequence<T>::getmember(int x) {
return memblock[x];
}
int main()
{
mysequence < int > myints;
myints.setmember(9);
cout << myints.getmember(0);
}
This should indicate the problem:
template <class T, int N>
void mysequence<T, N>::setmember(T value) {
if (sizeof(memblock) == 1) memblock[0] = value;
else {
int y = sizeof(memblock) / sizeof(memblock[0]);
cout << "y: " << y << endl;
memblock[y] = value;
}
}
When called with:
myints.setmember(1);
myints.setmember(5);
myints.setmember(8);
I get the output:
y: 14
y: 14
y: 14
y is always the same because it's calculated by finding the length of the underlying storage array, not the number of elements actually stored in that array.
Given that we aren't filling in the elements of the array correctly the values we get when we call getmember will be pulling out uninitialized elements of the array. This is where you get the garbage values such as -858993460 from (note that on my machine I got a different value here which is in line with the UB we have here).
If you want to support a push_back type of method that doesn't require an index to be passed in with the value then you need to change your design to have a field that keeps track of how many elements you have inserted into the array so far.
Additionally I'd make some checks to see if there is enough space to actually put the newest element into the back of the array before doing it.
You have misunderstood the functionality of push_back. What push_back does is that it adds elements to the end of an array, even an empty array. So I have modified some functionality of your functions and indicated them in the comments.
#include <iostream>
#include <algorithm>
using namespace std;
template <class T, int N>
class mysequence{
T *memblock;//You will need a pointer for enabling the resizing of the array
int size;//This is used to keep track of the size of the array
public:
mysequence();
mysequence(const mysequence &src);
~mysequence();
void setmember(T value);
T getmember(int x);
void push_back(T value);
mysequence& operator=(const mysequence &src);
};
template <class T, int N>
void mysequence<T, N>::mysequence(){
size = N;
memblock = new T[N];
}
template <class T, int N>
void mysequence<T, N>::mysequence(const mysequence<T,N> &src){
size = src.size
memblock = new T[size];
copy(src.memblock, src.memblock+size, memblock);
}
template <class T, int N>
void mysequence<T, N>::~mysequence(){
delete[] memblock;
}
template <class T, int N>
void mysequence<T, N>::setmember(T value) {//this setmember function just sets the last element of the array to the value specified
if( size > 0 ){
memblock[size-1] = value;
}
}
template<class T, int N>
void mysequence<T,N>::push_back(T value){
T *pointer = new T[size+1];
copy(memblock, memblock+size, pointer);
pointer[size] = value;
delete[] arr;
arr = pointer;
size++;
}
template <class T, int N>
T mysequence<T, N>::getmember(int x) {
return memblock[x];
}
template<class T, int N>
mysequence<T,N>& mysequence<T,N>::operator=(const mysequence<T,N> &src){
T *pointer = new T[src.size];
copy(src.memblock, src.memblock+src.size, pointer);
delete[] arr;
arr = pointer;
size = src.size;
}
int main()
{
mysequence < int, 14 > myints;
myints.setmember(9);
cout << myints.getmember(13)<<endl;//the indexing is one less. so to access the last element, you need to specify 13. As other values are not initialized, they will have garbage value in them
myints.push_back(1);
cout<<myints.getmember(14)<<endl;
return 0;
}
I call a function like this:
void do_work(void* ptr, size_t size)
{
//do things with ptr and size
}
The question is: is it possible to call the function like do_work(one); and somehow get the sizeof inside the function?
Given one is a static array (int one[10];) or non array (int one;), is it possible now?
In C++ you could use template argumment deduction to get the type of the pointer (the pointed type) and then compute its sizeof:
template<typename T>
void do_work( T* ptr )
{
constexpr std::size_t size = sizeof(T);
}
You can use a function template:
template <typename T>
void do_work(T* stuff)
{
const size_t s = sizeof(T);
....
}
Edit: version for arrays:
template<typename T, size_t N >
void do_work(T (&array)[N] )
{
const size_t s = N * sizeof(T);
}
No, not for void *, you will not be able to compute the size of the object whose address is passed.
template <typename T>
void do_work(T * t) { do_work(t, sizeof(T)); }
template<typename T>
void do_work(T* ptr)
{
size_t size = sizeof(T);
size = sizeof(*ptr); // or this
}
I don't know C++, this question appeared before me, probably because it previously had the C tag. But here's something that you could do in C, probably also in C++:
#define do_work(_a_) do_work_func(_a_, sizeof(_a_))
void do_work_func( void* ptr, size_t size )
{
//do things with ptr and size
}
Which would work out, given that one in do_work( one ); call had been defined as an array.
If I get you right you might want:
#include <iostream>
void do_work(void* ptr, size_t size)
{
std::cout << "Size: " << size << '\n';
}
template <typename T>
void do_work(T& single) {
do_work(&single, 1);
}
template <typename T, std::size_t N>
void do_work(T (&array)[N]) {
do_work(array, N);
}
template <typename T>
void do_work(T* multiple, std::size_t n) {
do_work(static_cast<void*>(multiple), n);
}
int main()
{
int a = 1;
int b[] = { 1, 2, 3 };
int* c = b;
do_work(a);
do_work(b);
do_work(c, 3);
}
Note: Why the void* ?
In the following:
int c[10] = {1,2,3,4,5,6,7,8,9,0};
printArray(c, 10);
template< typename T >
void printArray(const T * const array, int count)
{
for(int i=0; i< count; i++)
cout << array[i] << " ";
}
I am a little confused why the function signature of the template function makes no reference to array being an array by using [], so something like const T * const[] array.
How could one tell from the template function signature that an array is being passed and not just a non-array variable??
You cannot tell for sure. You would have to read the documentation and/or figure it out from the names of the function parameters. But since you are dealing with fixed sized arrays, you could have coded it like this:
#include <cstddef> // for std::size_t
template< typename T, std::size_t N >
void printArray(const T (&array)[N])
{
for(std::size_t i=0; i< N; i++)
cout << array[i] << " ";
}
int main()
{
int c[] = {1,2,3,4,5,6,7,8,9,0}; // size is inferred from initializer list
printArray(c);
}
An array has a size. To create a reference to an array, you need to provide the size statically. For example:
template <typename T, std::size_t Size>
void printArray(T const (&array)[Size]) {
...
}
This functions takes the array by reference and you can determine its size.
You could try something like the following:
template< std::size_t N>
struct ArrayType
{
typedef int IntType[N];
};
ArrayType<10>::IntType content = {1,2,3,4,5,6,7,8,9,0};
template< std::size_t N >
void printArray(const typename ArrayType<N>::IntType & array)
{
//for from 0 to N with an array
}
void printArray(const int * & array)
{
//not an array
}
Raxvan.