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.
Related
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.
Following an example from Scott Meyer's "Modern C++", I'd exploit std::array size deduction with templates. I'm stumbled trying to compile my myUsage function usage.
#include <array>
#include <iostream>
template <typename T, std::size_t N>
constexpr std::size_t arraySize(T (&) [N]) noexcept
{
return N;
}
void scottUsage()
{
int b[5];
std::array<short, arraySize(b)> c;
std::cout << arraySize(b) << " = " << c.size() << "\n";
}
template <typename T, std::size_t N>
void myUsage(T & arr [N])
{
for (auto i=0; i<arraySize(arr); i++)
std::cout << arr[i] << "\t";
}
int main()
{
scottUsage();
int a[7];
myUsage(a);
}
So two questions arise:
(side question) What's (&) for? Removing would trigger error: creating array of references, which it seems to be forbidden
What's wrong with myUsage signature?
What's (&) for?
It makes the argument a reference. If it wasn't a reference, then it would be an array. But function arguments are not allowed to be arrays, and such declarations are adjusted to be a pointer to an element of the array. Since this type does not carry information about size of the array, it is not useful to the size deduction.
Removing would trigger error: creating array of references, which it seems to be forbidden
void myUsage(T & arr [N])
Indeed, the parentheses are mandatory. They are needed to signify that the reference applies to the array and not the element type of the array. T&[N] is an array of references (which is not allowed) while T(&)[N] is a reference to an array.
What's wrong with myUsage signature?
Arrays of references are not allowed.
myUsage() needs to use T (&arr) [N].
template <typename T, std::size_t N>
void myUsage(T (&arr) [N])
The parentheses tell the compiler that the reference applies to the array itself and not to the element type of the array.
Also, both arraySize() and myUsage() should take a reference to const data:
template <typename T, std::size_t N>
constexpr std::size_t arraySize(const T (&) [N]) noexcept
...
template <typename T, std::size_t N>
void myUsage(const T (&arr) [N])
BTW, arraySize() is not needed from C++17 onward, use std::size() instead:
#include <array>
#include <iostream>
#include <iterator>
void scottUsage()
{
int b[5];
std::array<short, std::size(b)> c;
std::cout << std::size(b) << " = " << c.size() << "\n";
}
template <typename T, std::size_t N>
void myUsage(const T (&arr) [N])
{
for (auto i = 0; i < std::size(arr); ++i)
std::cout << arr[i] << "\t";
/* better:
for (const T &value : arr)
std::cout << value << "\t";
*/
}
int main()
{
scottUsage();
int a[7];
myUsage(a);
}
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* ?
Is there a way to use the new std::array type polymorphically in the size of the array? That is, if I have a function of the form
void DoSomething(std::array<int, 5>& myArray) {
/* ... */
}
Then is it mathematically well-defined to do the following (even if it's not legal C++ code?)
std::array<int, 10> arr;
DoSomething(arr);
Imof this is mathematically well-defined, is there a way to write std::array such that its array elements are contiguous and this code compiles? The only technique I could think of would be to have some weird template metaprogram where std::array<T, N+1> inherits from std::array<T, N>, but I don't believe that forces the array elements to be contiguous.
Directly? No.
You can, however, use compile-time polymorphism to achieve something very similar, and you can write a reference wrapper that makes it easier to work with in the code:
#include <array>
#include <cstddef>
template <typename T, std::size_t N>
struct ref_array_of_at_least
{
template <std::size_t M>
ref_array_of_at_least(T (&a)[M])
: data_(a)
{
static_assert(M >= N, "Invalid size");
}
template <std::size_t M>
ref_array_of_at_least(std::array<T, M>& a)
: data_(&a[0])
{
static_assert(M >= N, "Invalid size");
}
T* data_;
};
Used as:
void f(ref_array_of_at_least<int, 5>) { }
int main()
{
std::array<int, 5> x;
std::array<int, 6> y;
std::array<int, 4> z;
f(x); // ok
f(y); // ok
f(z); // fail
}
(You'd need to add some operator[] overloads and such to ref_array_of_at_least, and it needs some work to make it const correct, but it's a start that demonstrates the possibility of what you are seeking.)
If this was a requirement, one approach is a conversion operator to the required type:
#include <iostream>
template <typename T, int N>
struct Array
{
Array() { for (int i = 0; i < N; ++i) x[i] = 0; }
template <int N2>
operator Array<T, N2>&()
{
// for safety, static assert that N2 < N...
return reinterpret_cast<Array<T, N2>&>(*this);
}
int size() const { return N; }
T x[N];
friend std::ostream& operator<<(std::ostream& os, const Array& a)
{
os << "[ ";
for (int i = 0; i < N; ++i) os << a.x[i] << ' ';
return os << ']';
}
};
void f(Array<int, 5>& a)
{
a.x[a.size() - 1] = -1;
}
int main()
{
Array<int, 10> a;
std::cout << a << '\n';
f(a);
std::cout << a << '\n';
}
I wouldn't recommend it though: pretty horrid. A more explicit mechanism seems a lot less prone to misuse, as well as being more powerful - something vaguely like:
template <size_t N2>
Array<T,N2>& slice(size_t first_index)
{
return *(Array<T,N2>*)(data() + first_index);
}
// usage...
f(a.slice<5>(3)); // elements 3,4,5,6,7.
(clean up the casting for extra points :-/)
No, but you can fake it:
// Hide this function however you like: "detail" namespace, use "_detail"
// in the name, etc.; since it's not part of the public interface.
void f_detail(int size, int *data) {
use(data, /* up to */ data + size);
}
int const f_min_len = 5;
template<int N>
void f(int (&data)[N]) {
static_assert(N >= f_min_len);
f_detail(N, data);
}
template<int N>
void f(std::array<int, N> &data) {
static_assert(N >= f_min_len);
f_detail(N, &data[0]);
}
This is a complete example, and should work exactly as presented. You'd only have to change the data type from int (or make it a template parameter) and add const as required.