Why does S have to be const in this code? - c++

Why doesn't the below code compile? It says that S has to be const as one of the major errors.
template <int S>
class Array
{
int size;
int items [S];
public:
Array(void) :size(S){}
};
void main()
{
int S= 30;
Array <5+S> array;
}

Nontype template parameters must be constexpr, i.e., they have to be known at compile time. Hence, S must be declared as constexpr int.

Related

C++ template parameter inference

As an exercise I am trying to write an array implementation using templates, but with a function pointer as a template parameter. This function would be called every time the array gets indexed.
template<typename T, int size>
using Array_fnIndex = void (*)(int index);
template<typename T, int size, typename Array_fnIndex<T, size> fnIndex>
struct Array {
T data[size];
T& operator[](int index) {
fnIndex(index);
return data[index];
}
};
// example index function
template<typename T, int size>
void checkIndex(int index) {
assert(index < size);
}
int main() {
Array<int, 10, checkIndex<int, 10>> a; // this works
Array<int, 10, checkIndex<int, 11>> b; // this also works, not what I want
Array<int, 10, checkIndex> c; // this doesn't work, but what I want
return 0;
}
The last Array declaration in the main function is what I would like, where the template parameters of checkIndex match the previous template parameters in Array. However this doesn't compile (using Microsoft compiler). I get the following error:
error C2440: 'specialization': cannot convert from 'void (__cdecl *)(uint)' to 'void (__cdecl *)(uint)'
note: None of the functions with this name in scope match the target type
Is there any way to get the desired result where the template parameters for the provided function get inferred from the other parameters?
May not be applicable in your real use case, but I suggest a callable object containing a function that does the check:
template<typename T, int size, typename fnIndex>
struct Array {
T data[size];
T& operator[](int index) {
fnIndex{}.template check<size>(index);
return data[index];
}
};
struct checkIndex {
template<int size>
void check(int index) {
assert(index < size);
}
};
int main() {
Array<int, 10, checkIndex> c;
return 0;
}
wandbox example
Let's analyze fnIndex{}.template check<size>(index):
fnIndex{} // make a temporary object of type `fnIndex`
.template check<size>(index) // call its `check` method using `size`
// as a template argument and `index` as
// as a function argument
The .template disambiguation syntax is required because the compiler does not know what check means - it could be a field and the line could be interpreted as:
fnIndex{}.check < size > (index)
where < is the less-than operator, > is the greater-than operator, and (index) is an expression.
Using .template tells the compiler that we want to invoke a template method.

Type-dependent constant in template function

I want a static array in a templated function whose length depends on the type with which the function is specialized. My first attempt was:
Header:
template<typename T>
struct Length {
const static size_t len;
};
template<typename T>
void func(){
static T vars[Length<T>::len]; // len not const. according to compiler!
// ...
}
Source file:
template<> const size_t Length<double>::len = 2;
template<> const size_t Length<float>::len = 1;
// ...
However, g++ does not compile this and complains
error: storage size of ‘vars’ isn’t constant
So, what exactly is the problem here? I know that the size of a fixed-length array needs to be a constant and known on compile time, but that seems to be the case here.
When I write
const size_t len = 2;
void func(){
static double vars[len];
}
it compiles without problems.
Question:
What is wrong with the code and which alternatives are there for achieving the desired behavior? I would not like to allocate the memory during runtime...
For a const variable to be considered a compile-time constant (formally, a constant expression), its value must be available at point of use. Which means the specialised definitions would have to go to the header file.
If done as just specialisations of the member, as you did, I believe that would give you a multiple-definition error. You should be fine with specialising the entire class template, and keeping the static member definition inline:
template<typename T>
struct Length;
template <>
struct Length<double>
{
static const size_t len = 2;
};
As a side note, your program was originally invalid: an explicit specialisation must be declared before use. Which means you'd have to at least declare the specialisation of len in the header (or everywhere you intended to use it).
The following code compiles fine for me with g++ 4.6.3 and produces the output
2
1
array.cpp:
#include <cstddef>
#include <iostream>
template<typename T>
struct Length {
const static size_t len;
};
template<typename T>
void func(){
static T vars[Length<T>::len];
std::cout << (sizeof(vars) / sizeof(*vars)) << std::endl;
}
template<> const size_t Length<double>::len = 2;
template<> const size_t Length<float>::len = 1;
int main(){
func<double>();
func<float>();
}
$ make array
g++ array.cpp -o array
$ ./array
2
1

How to initialize a member array in a template

I would like to have an array which has a length that depends on the parameter of my template, but I keep getting the "expected constant expression" error.
enum MyEnum
{
FIRST,
OTHER
};
template<MyEnum e>
struct MyTemplate
{
static const int arrSize;
int myArr[arrSize]; // error C2057: expected constant expression
// int myArr[e == FIRST ? 4 : 10]; // works, but isn't very readable...
};
template<>
const int MyTemplate<FIRST>::arrSize = 4;
template<>
const int MyTemplate<OTHER>::arrSize = 10;
The compiler I must use does not support constexpr, or any other C++ 11 features, and I also cannot pass the array size as a template parameter.
edit: I also must not use new.
Thanks
In some cases like this, I'll add a function get_array_size<e>(). Since you say you don't have constexpr, there's still decent possibilities:
//I call this a pseudo-template-function, but there's probably better names
template<MyEnum> struct GetArraySize; //compiler error for default
template<> struct GetArraySize<FIRST> {static const int value=4;};
template<> struct GetArraySize<OTHER> {static const int value=10;};
template<MyEnum e>
struct MyTemplate
{
static const int arrSize = GetArraySize<e>::value;
int myArr[arrSize];
};
http://coliru.stacked-crooked.com/a/f03a5fa94a038892
Are we reinventing the wheel here ? Enums are compile time constants. Just do this :
enum MyEnum
{
FIRST = 4,
OTHER = 10
};
template<MyEnum e>
struct MyTemplate
{
int myArr[e];
};
demo

g++ (4.7.2) bug or feature, when initializing static arrays at compile-time?

Okay, so I was trying to do something clever by initializing a bunch of constexpr static int const arrays at compile-time. Even though the runtime-performance is not at all governed by initializing these arrays, it seemed like a fun little exercise. I wrote a test-setup to see if it was possible, and I ended up being able to do this:
struct Test
{
constexpr static int const array[10] = Array<int, 10, 0, Increment>::array;
};
constexpr int const Test::array[10];
int main()
{
cout << Test::array[3] << '\n';
}
Here, Array has a static member called array which contains 10 ints, starting at 0, where the value of each subsequent element is determined by a Template-Metaprogramming functor called Increment (i.e. {0, 1, ..., 9}). As expected, the program prints out the number 3.
Awesome, right? I can just write functors now and initialize arrays will all kinds of funky patterns at compile-time. Next step: un-hardcode the array-size 10 by making Test a class-template like so:
template <size_t Size>
struct Test
{
constexpr static int const array[Size] = Array<int, Size, 0, Increment>::array;
};
template <size_t Size>
constexpr int const Test<Size>::array[Size];
int main()
{
cout << Test<10>::array[3] << '\n';
}
However, all of a sudden it doesn't compile anymore with the message:
test.cc:43:72: error: array must be initialized with a brace-enclosed initializer
Why does this happen? Is there a reason that this kind of initialization has become invalid once I turn the class into a class-template, or have I stumbled upon something unimplemented/buggy in GCC?
FYI, I can post the rest of my code (the implementation of Array for example) on request. For now I think this should be enough.
EDIT The error can be reproduced with a different, trivial, implementation of Array to save some space here:
template <size_t Size>
struct Array
{
constexpr static int const array[Size] = {};
};
template <size_t Size>
struct Test
{
constexpr static int const array[Size] = Array<Size>::array;
};
Following is illegal;
static const int a[10] = {};
static const int b[10] = a; // Illegal
So the bug of gcc is in fact for the non template case.
You may use std::array instead of C-array.

C++ static const array initialization in template class

I have the following template class:
template <unsigned N>
class XArray {
static const int Xdata[N];
};
I want to initialize the static const array for each XArray<N> I used, for example, let XArray<N>::Xdata = {1, 2, 3, ..., N}. How to make it?
you declared a static const int array in your class,so you must define the static member out of the class declaration,just like this:
template<unsigned N>
class XArray
{
public:
static const int array[N];
};
template<unsigned N>
const int XArray<N>::array[N] = {1,2,3,4,5};
But something you must pay attention to is that: when you use this template you must make sure that the "N" bigger than the number of your initialized array;
EDIT:
It seems someone have already provided the solution for your problem in other question, and the answer is quite the same as mine.
Also, for more generic answer, you can check out answers to this question.
Code
If you don't mind using C++11 features, then variadic templates may come handy:
template <unsigned ...Args>
struct XArrayData
{
static const int Values[sizeof...(Args)];
};
template<unsigned N, unsigned ...Args>
struct _XArrayGenerator
{
typedef typename _XArrayGenerator<N - 1, N, Args...>::Xdata Xdata;
};
template<unsigned ...Args>
struct _XArrayGenerator<1, Args...>
{
typedef typename XArrayData<1, Args...> Xdata;
};
template<unsigned N>
struct XArray
{
typedef typename _XArrayGenerator<N>::Xdata Xdata;
};
template <unsigned ...Args>
const int XArrayData<Args...>::Values[sizeof...(Args)] = { Args... };
Explanation
XArray template struct takes the number of array elements as a template parameter (N). In the compilation time, it uses _XArrayGenerator to generate template paremeter list with N consecutive numbers. It begins with the number N, and then recursively uses itself until it reaches 1. At this point, the template parameter list looks like this:
1, 2, ..., N
The last thing to do is to pass these parameters to XArrayData. The last line of the code (definition of the actual array) uses the parameters to initialize the array.
Usage
for (int i = 0; i < 3; ++i)
cout << XArray<3>::Xdata::Values[i] << endl;
Output:
1
2
3
You can initialize as shown below. See inline comments for my explanation.
template <unsigned N>
class XArray {
private:
static const int Xdata[N];
public:
//I added this for illustration purpose
void print()
{
for (int i = 0; i < N; ++i)
{
std::cout << Xdata[i] << std::endl;
}
}
};
//you can initialize like this
//automatic size counting works with static arrays
//here I initialize with 3 elements
//make sure you don't use N < 3 anywhere
template <unsigned N>
const int XArray<N>::Xdata[] = {1,2,3};
int main(void)
{
XArray<3> obj1; //N = 3: This is okay.
XArray<8> obj2; //N > 3: This is okay. Remaining elements will be 0.
XArray<2> obj3; //N < 3: This is also okay.
obj1.print();
obj2.print();
obj3.print(); //but this will give compilation error
return 0;
}