Pointer to array as template parameter - c++

I'd like to instanciate my object with template parameters only. One of the parameters is a pointer to array, and I'm looking for the correct syntax.
const MyCustomType* array[2] = { &object1, &object2 };
OBJ1 < 10, 10, array > myobj1;
Below, a sample of the class OBJ1.
template < int a, int b, /* help ! */ >
class OBJ1
{
public:
OBJ1();
~OBJ1();
private:
//methods
};
What's the right syntax to use the third template parameter ? Is that even possible ?

For an array of type int of size n the syntax is
int (*paramname)[N];
Or with a helper type alias
template<typename T>
using type = T;
Then
type<int[N]> *paramname;
In your case you pass a pointer to the arrays first element though, and not a pointer to the array. You need to prefix the array name with & to do the latter when passing the array.

Related

Pass initializer list to constructor?

I have written my own array template class "CArray" and want to pass something like { 1, 2, 3 } to one of its constructors so that I can create an array class instance "CArray a ( {1,2,3}, numElems );
However, I fail to find the proper syntax for the constructor.
template <class _T>
class CArray {
public:
_T* buffer;
int length;
CArray () : buffer (nullptr), length (0) {}
void Create (int l) {
buffer = new _T[length = l];
}
CArray (_T const* values, int numElems) {
Create (numElems);
memcpy (buffer, values, numElems * sizeof (_T));
}
};
CArray<int> a = CArray<int>( {1,2,3}, 3 );
This neither works for MSVC 19 latest nor gcc 11.2. How would I have to write my constructor to make this work?
Is there a possibility to omit the "numElems" parameter and get the initializer length (elem count) from the compiler?
Yes, the compiler can deduce the size of the brace-init list, if the argument is a reference to an array:
template<int numElems>
CArray (_T const (&values)[numElems])
{
// ...
}
and an object is created like this:
CArray<int> a = CArray<int>({1,2,3});
demo

Expand variadic template to array of static members

I've defined a base class template:
template<class actual_class>
class base
{
public:
static const int value;
}
and the definition of value depends on the actual_class tparam.
Next, I have a bunch of derived classes from base, let's call them a and b. Let's also say that a::value = 5 and b::value = 10.
Now, in a method template where I need to access the static values from a parameter pack. I'd like to have them in a vector.
template<class... derived_from_bases>
void foo(irrelevant_class<derived_from_bases...> irrelevant)
{
// std::vector<int> values = { ... }
...
}
For the function called with < a, b > tparams I'd like the values vector to look like this:
std::vector<int> values = {5 /* a::value */, 10 /* b::value */};
Also having an std::array instead of an std::vector would be a nice touch.
Thank you for your help in advance.
For a vector, you just need
std::vector<int> values = { derived_from_bases::value... };
If you have C++17, you can get a std::array the same way like
std::array values = { derived_from_bases::value... };
and CTAD will deduce the type and size of the array for you. If you do not have C++17, then you can use
std::array<int, sizeof...(derived_from_bases)> values = { derived_from_bases::value... };

Automatic deduction of type of added array

I am quite new to c++ and I try to create an Array class in c++17 with the use of templates. In this class I overload the + operator, in such a way that it can add Arrays of multiple types. It does work so far and I am able to add arrays of different e.g. float and int type together. However, I am having some trouble with how to define the type of the new array, which is the result of the addition.
Let's say the arrays which I add are of type float and int. Then the new array should also be float. However, on forehand I dont know which array has the float type, the first one or the second one, so I can't create a new array with typename T or U.
Also, if due to coindicdence two float arrays add up together to only int values (e.g. 1.5 + 3.5 = 5(int) ), then the new array should be of type int.
Basically in summary, I try to define the type of the new array based on the type of the content after addition.
I came across some solutions that include decltype. However I can't manage to find a way how to include this for multiple values, since the array has more than one value. In my current code I create the new array based on the type T. However, if in a case T is of type int and U of type float, the result is not correct.
Any advice or tips are much appreciated.
Thanks in advance,
template <typename T>
class Array {
public:
T* content;
int length;
// Default Constructor
Array() : content(nullptr), length(0) {}
// Constructor when length is provided
Array(int length) : content(new T[length]), length(length) {}
// Constructor (using initializer list)
Array(std::initializer_list<T> list) : Array((int)list.size()) {
std::uninitialized_copy(list.begin(), list.end(), content);
}
// Obtain content at index i
float& operator[](int i) { return content[i]; }
// Adding arrays
template <typename U>
Array& operator+(Array<U>& other) {
Array<T>* new_array = new Array(other.length);
for (auto i = 0; i < other.length; i++)
new_array->content[i] = this->content[i] + other.content[i];
return *new_array;
}
};
With decltype, your operator + might look like:
template<typename U>
auto operator+(const Array<U>& rhs)
-> Array<std::decay_t<decltype((*this)[0] + rhs[0])>>
{
Array<std::decay_t<decltype((*this)[0] + rhs[0])>> res(rhs.length);
for (auto i = 0; i != rhs.length; i++) {
res[i] = (*this)[i] + rhs[i];
}
return res;
}
Demo

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.

Factory method returning an concrete instantiation of a C++ template class

I have a class
template <unsigned int N>
class StaticVector {
// stuff
};
How can I declare and define in this class a static factory method returning a StaticVector<3> object, sth like
StaticVector<3> create3dVec(double x1, double x2, double x2);
?
"How can I declare and define in this class"
In what class? You've defined a class template, not a class. You can't call a static function of a class template itself, you have to call a particular version of the static function that's part of a real class.
So, do you want the template (and hence all instantiations of it) to have a function returning a StaticVector<3>, or do you want one particular instantiation of that template to have a function returning a StaticVector<3>?
If the former:
template <unsigned int N>
struct SV {
int contents[N];
static SV<3> get3dVec(int x, int y, int z) {
SV<3> v;
v.contents[0] = x;
v.contents[1] = y;
v.contents[2] = z;
return v;
}
};
int main() {
SV<3> v = SV<1>::get3dVec(1,2,3);
}
works for me.
If the latter (you only want get3dVec to be a member of SV<3>, not of all SV<whatever>), then you want template specialisation:
template <unsigned int N>
struct SV {
int contents[N];
};
template<>
struct SV<3> {
int contents[3]; // must be re-declared in the specialization
static SV<3> get3dVec(int x, int y, int z) {
SV<3> v;
v.contents[0] = x;
v.contents[1] = y;
v.contents[2] = z;
return v;
}
};
int main() {
SV<3> v = SV<1>::get3dVec(1,2,3); // compile error
SV<3> v = SV<3>::get3dVec(1,2,3); // OK
}
If for no other reason than to make the calling code look nicer by omitting the basically irrelevant template parameter, I agree with Iraimbilanja that normally a free function (in a namespace if you're writing for re-use) would make more sense for this example.
C++ templates mean that you don't need static functions as much in C++ as you do in Java: if you want a "foo" function that does one thing for class Bar and another thing for class Baz, you can declare it as a function template with a template parameter that can be Bar or Baz (and which may or may not be inferred from function parameters), rather than making it a static function on each class. But if you do want it to be a static function, then you have to call it using a specific class, not just a template name.
Something like :
template< unsigned int SIZE >
StaticVector< SIZE > createVec( const std::tr1::array< double, SIZE >& values )
{
StaticVector< SIZE > vector;
for( int i = 0; i < values.size(); ++i ) // here assuming that StaticVector have [] operator to access values on write
{
vector[i] = values[i];
}
return vector;
}
... or a variant would certainly work. (did'nt test it)
Usage would be:
std::tr1::array< double, 3 > vectorValues = { 10.0, 10.0, 42.0 };
StaticVector<3> vector1 = createVector( vectorValues ); // if the compiler can guess the size from the array information
StaticVector<3> vector2 = createVector<3>( vectorValues ); // if you have to specify the size
An alternative would be to replace std::tr1::array by a basic array but it would be using raw arrays at your own risks :)
First, i believe you originally mean to return
StaticVector<N>
Instead of always a specialization with N==3 . So, what you want to do is writing it like this:
template <unsigned int N>
class StaticVector {
public:
// because of the injected class-name, we can refer to us using
// StaticVector . That is, we don't need to name all template
// parameters like StaticVector<N>.
static StaticVector create3dVec(double x1, double x2, double x2) {
// create a new, empty, StaticVector
return StaticVector();
}
};
If you really want to always return a 3dVector, you would probably want to restrict it to N==3, so that for example StaticVector<4>::create3dVec doesn't work. You can do that using the technique described here.
If you want to have a function like createVec that works with any size, you probably want to replace the parameters with an array. You can do otherwise, but that's advanced and requires some macro tricks applied with boost::preprocessor. It's not worth it i think. The next C++ version will provide variadic templates for this purpose. Anyway,consider using something like this:
I think it would only complicate this unnecessarily here. A quick solution is to use a boost::fusion::vector instead, putting it into the class template instead of the version above:
static StaticVector createVec(double (&values)[N]) {
// create a new, empty, StaticVector, initializing it
// with the given array of N values.
return StaticVector();
}
You could use it with
double values[3] = {1, 2, 3};
StaticVector<3> v = StaticVector<3>::createVec(values);
Note that it accepts an array by reference. You can't give it a pointer. That is because it matches the use of parameters: You couldn't provide less or more arguments for the other way too. It will also protect you from cases like this:
// oops, values is a null pointer!
StaticVector<3> v = StaticVector<3>::createVec(values);
An array never can be a null pointer. Of course if you like, you can always change the array parameter to a pointer. It would just be my personal preference :)
#litb
I wanted to return a 3-element vector. The reason is that these vectors are supposed to be geometric entities, so there is no need for N to be too high (I'm not a string physicist, so I don't work in 11-dimensional spaces). I wanted to create a template to avoid duplicating code but keep 1-, 2- and 3-dimensional vectors as separate types.