I have a function template that is supposed to take a vector and produce random numbers inside it. However, when I print entire vector, it's all zeros.
code:
const int smallSize = 20;
// declare small vector
vector <int> smallVector(smallSize);
genRand(smallVector, smallSize);
// make copy of small vector
vector<int> copySmallVector = smallVector;
// function template for generating random numbers
template<class T, class B>void genRand(T data, B size)
{
for (B i = 0; i < size; i++)
{
data[i] = (1 + rand() % size);
}
}
You are generating random numbers in a copy of your vector, and then throwing it away when the function returns.
Change:
template<class T, class B>void genRand(T data, B size)
to:
template<class T, class B>void genRand(T &data, B size)
^^^^^^^
Related
My question might sound weird because of how precise the problem is.
So, for the context, i have created a multidimensional array class :
template <typename TYPE>
requires std::integral<TYPE> || std::floating_point<TYPE>
class MultiArray {
int mDim = 0;
std::vector<int> mShape = {};
std::vector<TYPE> mArray = {};
[METHODS]
}
(A multiArray object need a dimension, a shape that is the same size of the dimension and the "mArray" is the array that have all the elements of the MultiArray which type is dependent of the template)
I'm trying to do an "=" operator overloading but my MultiArray objects can do maths operations (like numpy in python) so their type can change.
I want to be able to do things like this :
MultiArray<int> M(2,5); //Creating a 5x5 matrix of zeroes
M = M+2.12; //I want M to be 5x5 matrix of 2.12
BUT my M is an "int" MultiArray and, because i'm adding a float, "M+2.12" return a "double" MultiArray so I have to convert my M into a MultiArray "double".
With the operator overloading, I tried :
template <typename TYPE>
void operator=(MultiArray<TYPE> MultiArr) {
// defining the return type
using TYP_RE = std::conditional_t<std::is_floating_point<TYPE>::value, double, int>;
// we allocate the dimension and the shape
this->mDim = MultiArr.getDim();
this->mShape = MultiArr.getShape();
// we calculate the size of the 1-D mArray
int taille = 1;
for (int i = 0 ; i < mDim ; i++) {
taille *= this->mShape[i];
}
// we create a new array that will contain the new values
std::vector<TYP_RE> Array;
std::vector<TYPE>& Arr = MultiArr.getArray();
for (int i = 0 ; i < taille ; i++) {
Array.push_back(static_cast<TYP_RE>(Arr[i]));
}
// We allocate this new array into the old array
this->mArray.clear();
this->mArray.swap(Array);
}
The problem is with the last command. I can't swap a "double" vector with an "int" vector and I'm stuck here.
Have a nice day !
this->mArray.clear();
this->mArray.swap(Array);
should be
this->mArray.assign(Array.begin(), Array.end());
we create a new array that will contain the new values - this intermediate Array looks redundant.
Maybe you are looking for something like this?
template <class T, class U>
auto operator + (const MultiArray<T>& a, U b>)
-> MultiArray<decltype<declval<T>()+b>>
{
MultiArray<decltype<declval<T>()+b>> rslt;
// do the math;
return rslt;
}
And then
MultiArray<int> M(2,5); //Creating a 5x5 matrix of zeroes
auto N = M+2.12; //I want M to be 5x5 matrix of 2.12
I mean to build a vector of M N-sized-vectors, i.e., with K=M*N total elements, from a K-sized vector with a linear arrangement of the same set of values.
Moreover, I would do that in a class constructor, although I guess that is irrelevant.
My code is shown below.
What should I use in the pushback line?
template <int dim>
class vec2d {
public:
// Constructor - Version 1: 2D array as initializer
vec2d(const std::vector<std::vector<double> >& c);
// Constructor - Version 2: 1D array as initializer
vec2d(const std::vector<double>& c);
...
protected:
std::vector<std::vector<double> > _vec2d;
};
// Version 1: 2D array as initializer
...
// Version 2: 1D array as initializer
template <int dim>
vec2d::vec2d(const std::vector<double>& c)
{
for (size_t i = 0; i < (c.size() / dim); i++)
_vec2d.push_back(std::vector<double>(c(i * dim), c((i+1) * dim - 1))); // <- Fix this line
}
std::vector has a constructor that takes an iterator range. Using that you can change you loop to use that like
template <int dim>
vec2d::vec2d(const std::vector<double>& c)
{
auto begin = c.begin();
auto end = begin + dim;
for (size_t i = 0; i < (c.size() / dim); i++) {
_vec2d.emplace_back(begin, end);
begin += dim; // move range to next dim sized block
end += dim; // this could be moved into the for loops increment section
}
}
what about:
template <int dim>
vec2d::vec2d(const std::vector<double>& c)
{
for (auto i = c.begin(); i < c.end(); i+=dim )
_vec2d.push_back({i, i+dim});
}
or mixing with the hint from #NathanOlivier:
template <int dim>
vec2d::vec2d(const std::vector<double>& c)
{
for (auto i = c.begin(); i < c.end(); i+=dim )
_vec2d.emplace_back(i, i+dim);
}
we may debate if "emplace" is longer than "push" + "{}", but it should avoid a call to the move operator on the temporary vector passed as push_back() argument.
As pointed out, it assumes, based on your example that:
the c size is multiple of dim (or equal to dim*dim)
the _vec2d has been cleared before
As optimization you should add _vec2d.resize(c.size()/dim) or _vec2d.resize(dim) before performing the series of invocation of the push_back() method.
I have been reading this website for quite a while now but have just registered.
I have also used search which did not seem to be very helpful.
Here it is:
As I am having fun with C++ I come along "lazy evaluation" conception on the Internet. I am interested in creating something like a "lazy vector" which is defined with the function which takes N arguments and first N vector elements.
However, I have currently come across the issue with it. Let me point it out:
template<typename T, typename... A>
class llazy
{
public:
llazy(const std::function<T(A...)>& func, A... args) : func(func), vec({args...}), numArgs(sizeof...(args))
{}
const T& operator[](size_t index)
{
unsigned short tmp = 1;
//Here I want to stray from 2 elements to pass to function and make the number at least like 0-4
std::vector<size_t> v;
for (unsigned short i = 0; i < numArgs; i++)
v.emplace_back(vec.size() - tmp++);
//As you can see here, there are no issues(if the vector has enough elements ofc) with these two
unsigned long long i = vec.size() - 2, j = vec.size() - 1;
while (vec.size() < index + 1)
//So how do I pass all the vec[v[0]], vec[v[1]], etc... elements to the "func"?
//Maybe there is another way to achieve this, however, this is the Python which makes me think this is possible
vec.emplace_back(func(vec[i++], vec[j++]));
if (vec.size() >= index + 1)
return vec[index];
}
private:
const unsigned char numArgs;
const std::function<T(A...)> func;
std::vector<T> vec;
};
using ullong = unsigned long long;
int main()
{
llazy<ullong, ullong, ullong> l(std::function<ullong(ullong, ullong)>([](ullong i, ullong j) { return i + j; }), 1, 1);
l[20];
l[50];
l[1000];
return 0;
}
Thank you for your answers in advance.
UPD: Sure, the vector can be passed to the function, however, this makes the functions themselves a lot less readable(e.g. unsigned sum(unsigned, unsigned) is much more clear than unsigned sum(std::vector)).
How do I use the last N elements of a vector as a function parameters
You don't need to put those elements into a temporary vector.
Instead, the classical solution to that is to use a separate function (or a C++20 template lambda, if you feel fancy) with a std::index_sequence parameter (and a parameter pack of indices). With a pack expansion, you can easily extract last N elements from your vector, one way or another.
Something like this would work:
template <typename F, std::size_t ...I>
auto make_tuple_seq(std::index_sequence<I...>, F &&func)
{
return std::tuple{func(I)...};
}
int main()
{
std::vector v = {1,2,3,4,5,6};
const int n = 3;
auto t = make_tuple_seq(std::make_index_sequence<n>{},
[&](std::size_t i) {return v[v.size() - n + i];});
// Prints `456`.
std::cout << std::apply([](int x, int y, int z){return x*100 + y*10 + z;}, t) << '\n';
}
It shouldn't be hard to adapt this code for your needs.
Suppose I have this class:
template<class K, class Compare>
class findMax {
K* keyArray; // Supposed to be an array of K.
int size;
public:
findMax (K n, Compare init); // Here I would like to initialize the array.
~findMax ();
K& Find(K key, Compare cmp); // Return an elemnt in the array according to a condition.
void printArray(Compare print); // Print the array according to a condition.
};
I want each cmp function to be different when I implement the constructor, Find and printArray.
For example:
template<class K, class Compare>
findMax<K, Compare>::findMax(int n, Compare init) {
keyArray = new K [n];
init(keyArray, n);
}
where init is a function I implement in my source file, like this for example:
// Init will initialize the array to 0.
void init (int* array, int n) {
for (int i=0; i<n; i++)
array[i] = 0;
}
Although, I want to be able to send a different function to Find for example, that compares between two elements. I'm having trouble figuring out how because when I create a new findMax object, like findMax<int, UNKNOWN> f, what do I put instead of UNKNOWN?
Try this -
#include <iostream>
#include <functional>
using namespace std;
template<class K, class Compare>
class findMax {
K* keyArray; // Supposed to be an array of K.
int size;
public:
findMax (K n, Compare init){init();}; // Here I would like to initialize the array.
~findMax (){};
template<typename Compare1>
K& Find(K key, Compare1 cmp){ cmp();}; // Return an elemnt in the array according to a condition.
template<typename Compare2>
void printArray(Compare2 print){print();}; // Print the array according to a condition.
};
int main() {
findMax<int,std::function<void()>> a{5,[](){cout<<"constructor"<<endl;}};
a.Find(5,[](){cout<<"Find"<<endl;});
a.printArray([](){cout<<"printArray";});
return 0;
}
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.