How should I change the code below so that Array<Index> array; is enough and the SIZE is automatically deduced from the enum?
Even if the enum changes, it is guaranteed that it contains SIZE referring to the correct size.
template <typename Enum, int N>
class Array {
public:
int& operator[](Enum index) { return array[index]; }
private:
int array[N];
};
enum Index { X, Y, SIZE };
int main() {
Array<Index, SIZE> array;
array[X] = 1;
return 0;
}
UPDATE: As for "Array<type> means you're creating an array of Type objects" (Jerry) and "the name of class template is a bit misleading" (Nawaz): actually I am creating CustomSqlQueryModel<TableColumns>. The above is just a simplified code, nothing more. Jerry and Nawaz are rigth: this simplified code is unfortunate.
You can write a traits class. This requires a bit of extra work each time you define a new enum type, but no extra work for each occurrence of Array<Index> in user code:
template<class Enum>
struct ArrayTraits;
template<class Enum>
struct Array {
int& operator[](Enum index) { return array[index]; }
private:
int array[ArrayTraits<Enum>::size];
};
enum Index { X, Y, SIZE };
template<>
struct ArrayTraits<Index> {
enum { size = SIZE };
};
int main() {
Array<Index> array;
array[X] = 1;
return 0;
}
One of the advantages of this is you can specialize the traits for external enums you don't control, as long as you know how to get the max size.
As stated, I don't think you can. If, however, you change it to something like:
struct Index {
enum { X, Y, SIZE};
};
Then your template could be something like:
template <class Enum>
class Array {
// ...
private:
int array[Enum::SIZE];
};
...and if the type you pass as Enum doesn't include some positive constant named SIZE,the instantiation won't compile. For the purpose at hand, you'd really kind of prefer that Index was a namespace, but since a namespace isn't a type, I don't think you can use it as a template argument.
I should add, however, that I'm not sure I like this idea at all -- most people are going to think Array<type> means you're creating an array of Type objects, and this is clearly something entirely different from that...
If you want only the size to be template argument, not the type , as from your example it seems that the type of the array would be always int, then why don't you implement this:
template <int size>
class Array {
public:
int& operator[](int index) { return array[index]; }
//Note this addition!
int operator[](int index) const { return array[index]; }
private:
int array[size];
};
int main() {
Array<10> array;
array[0] = 1;
array[1] = 2;
return 0;
}
Note this addition: it's better if you implement const version of operator[] too, so that const Array<> can use it to access the array elements, otherwise your class wouldn't work for const Array<>.
Related
I need to create an adapter C++ class, which accepts an integer index, and retrieves some types of data from a C module by the index, and then returns it to the C++ module.
The data retrieving functions in the C module are like:
int getInt(int index);
double getDouble(int index);
const char* getString(int index);
// ...and etc.
I want to implement an array-like interface for the C++ module, so I created the following class:
class Arguments {
public:
template<typename T> T operator[] (int index);
};
template<> int Arguments::operator[] (int index) { return getInt(index); }
template<> double Arguments::operator[] (int index) { return getdouble(index); }
template<> std::string Arguments::operator[] (int index) { return getString(index); }
(Template class doesn't help in this case, but only template member functions)
The adapter class is no biggie, but calling the Arguments::operator[] is a problem!
I found out that I can only call it in this way:
Arguments a;
int i = a.operator[]<int>(0); // OK
double d = a.operator[]<double>(1); // OK
int x = a[0]; // doesn't compile! it doesn't deduce.
But it looks like a joke, doesn't it?
If this is the case, I would rather create normal member functions, like template<T> T get(int index).
So here comes the question: if I create array-operator-overloading function T operator[]() and its specializations, is it possible to call it like accessing an array?
Thank you!
The simple answer is: No, not possible. You cannot overload a function based on its return type. See here for a similar quesiton: overload operator[] on return type
However, there is a trick that lets you deduce a type from the lhs of an assignment:
#include <iostream>
#include <type_traits>
struct container;
struct helper {
container& c;
size_t index;
template <typename T> operator T();
};
struct container {
helper operator[](size_t i){
return {*this,i};
}
template <typename T>
T get_value(size_t i){
if constexpr (std::is_same_v<T,int>) {
return 42;
} else {
return 0.42;
}
}
};
template <typename T>
helper::operator T(){
return c.get_value<T>(index);
}
int main() {
container c;
int x = c[0];
std::cout << x << "\n";
double y = c[1];
std::cout << y ;
}
Output is:
42
0.42
The line int x = c[0]; goes via container::get_value<int> where the int is deduced from the type of x. Similarly double y = c[1]; uses container::get_value<double> because y is double.
The price you pay is lots of boilerplate and using auto like this
auto x = c[1];
will get you a helper, not the desired value which might be a bit unexpected.
I just want to ask if there are any uses for passing variables in cpp as template arguments
template<int a> struct foo {
int x = a;
};
int main() {
foo<2> bar;
std::cout << bar.x;
}
Something like this compiles, works and cout's 2 but the same thing can be done by doing
struct foo {
int x;
foo(int a) : x(a) {}
};
int main() {
foo bar(2);
std::cout << bar.x;
}
So what is the point of using variables in template arguments? I can also see a big flaw in using the first method: the variable a uses memory and isn't destructed after x is changed, as it would be after the constructor is called in the second example. It might be helpful if you showed some reasonable uses for that.
When you pass a variable through a template argument, it can be used in compile time.
For example, if you need to create a statically sized array in your class, you could use the template argument to pass the size of your array:
template <int TSize>
class Foo {
[...] // Do whatever you need to do with mData.
private:
std::array<int, TSize> mData;
};
There are many uses for constants in template parameters.
Static Sizes
This is how you would start implementing something like a std::array.
template <typename T, size_t SIZE>
struct Array {
T data[SIZE];
}
Template parameters are always usable in a constexpr context, so they can be used as sizes for statically sized arrays.
Providing Compile-Time Parameters to Algorithms
Another use is parametrizing algorithms like in the following code sample.
We have a uint32_t in ARGB order but to store it in a file, we might need to reorder it to BGRA or RGBA. We know the order at compile time, so we could use an ArgbOrder template variable.
enum class ArgbOrder { ARGB, RGBA, BGRA };
struct ChannelOffsets {
unsigned a;
unsigned r;
unsigned g;
unsigned b;
};
// and we can get a constexpr lookup table from this enum
constexpr ChannelOffsets byteShiftAmountsOf(ArgbOrder format)
{
...
}
template <ArgbOrder order>
void encodeArgb(uint32_t argb, uint8_t out[4])
{
// We can generate the shift amounts at compile time.
constexpr detail::ChannelOffsets shifts = shiftAmountsOf(order);
out[0] = static_cast<u8>(argb >> shifts.a);
out[1] = static_cast<u8>(argb >> shifts.r);
out[2] = static_cast<u8>(argb >> shifts.g);
out[3] = static_cast<u8>(argb >> shifts.b);
}
void example() {
encodeArgb<ArgbOrder::BGRA>(12345);
}
In this example, we can select the appropriate lookup table at compile time and have zero runtime cost. All that needs to happen at runtime is 4 shifts.
Feature Toggles
We can use bool template variables to toggle features in our code, like for example:
template <bool handleZeroSpecially>
int div(int x, int y) {
if constexpr (handleZeroSpecially) {
return y == 0 ? 0 : x / y;
}
else {
return x / y;
}
}
I'm trying to write an implementation for hash map, I'm not allowed to use anything from stdlib except for iostream, string and cassert.
It needs to be generic, so the values that populate the buckets can be of any type. I need templates for this, but can't manage to pass the hash function in any way. This would be the header file:
template<typename Value, typename hashFunction>
class hashTable{
public:
hashTable(int size){
//Creates an empty vector of size on the table
}
define(Value v){
loads value in Vector[hashFunction(v)];
}
...
private:
Vector with all the elements
}
Note: I guess I don't need templates for the keys, do I?
I can't define the hash function inside my class because I'd have to make one that works with all types (string to int, int to int, double to int, etc). So I guess the only solution is to pass the function as argument in my main. This would be the main.
int hashF(int v){return v}
int main(){
hashTable<int,int,hashF> table(5);
}
But this doesn't work, g++ tells me "expected type but got hashF". I guess I could pass a pointer to a function, but that seems like a hack rather than a real solution. Is there a better way?
template<typename Value, int(*fun)(Value)>
class hashTable {
std::vector<Value> v;
public:
hashTable(std::size_t size) : v(size) { }
void define(Value &&val) { v[fun(val)] = val; }
};
Live Demo
Non function pointer way:
template<typename Value, typename F>
class hashTable {
std::vector<Value> v;
F fun;
public:
hashTable(std::size_t size, F fun_) : v(size), fun(fun_) { }
void define(Value &&val) { v[fun(val)] = val; }
};
Live Demo
Managed to get it working with Neil's advice. My hash.h:
template<typename C, typename D, typename H>
class Tabla {
public:
Tabla(int s){
cout << hashF(3) << endl;
size=s;
}
private:
H hashF;
int size;
};
My hash.cpp
struct KeyHash {
unsigned long operator()(const int& k) const
{
return k % 10;
}
};
int main(){
Tabla<int,int,KeyHash> tab(3);
return 0;
}
This example is just to show I'm able to use the function inside the template, then I'd have to code the define and delete functions that use that KeyHash.
Dunno why I have to wrap it like this, but it works. Found the specifics of it here
I need to create a template because I don't know what it's an array of. And it needs to be of the size that's passed in the constructor. So here is what I got and I got all sorts of errors. I am a beginner to C++ so any help is appreciated :)
template <typename T, int N>
class Array
{
public:
T& operator[](int index)
{
return data[index];
}
private:
int size;
T *data[N];
};
I think you understand what I'm trying to do. I also need to overload the subscript operator, as you can see. Not sure if I need a reference or a pointer or what. I did have a constructor but it wasn't working properly.
Here's a corrected version with a sample main as well:
#include <iostream>
using namespace std;
template <typename T, int N>
class Array
{
public:
T& operator[](int index)
{
// add check for array index out of bounds i.e. access within 0 to N-1
return data[index];
}
Array() {
data = new T[size = N];
}
~Array() {
if (data)
delete [] data;
}
private:
int size;
T *data;
};
int main(void) {
Array<int, 4> a;
a[0] = 5;
cout << a[0] << endl;
return 0;
}
From what I can see your template is containing an array of pointers, which from what I read and from your [] operator implementation is not what you intend to do. So first you should remove the * from :
T *data[N];
You should probably initialize the size of your template from your constructor, thus you should change :
T *data[N];
to :
T* data;
and :
template <typename T, int N>
to :
template<typename T>
Now that changes the implementation a bit, you should now write a constructor like that :
template<typename T>
Array(int n) {
data = new T[n];
}
and now you should also add a destructor like that :
~Array() {
delete[] data;
}
and there you go :)
However if you want to keep the size as an argument of the template your constructor go like :
template<typename T, int N>
Array() {
}
and the declaration of data :
T data[N];
As said in comments, you may want to use std::array in "real life" conditions, but as a training, implementing your own Array is a good thing to do, that's a training most computer schools do after all.
How to declare a constant array in class with constant class variable? Is it possible.
I don't want dynamic array.
I mean something like this:
class test
{
const int size;
int array[size];
public:
test():size(50)
{}
}
int main()
{
test t(500);
return 0;
}
the above code gives errors
No, it's not possible: As long as size is a dynamic variable, array[size] cannot possibly be implemented as a static array.
If you like, think about it this way: sizeof(test) must be known at compile time (e.g. consider arrays of test). But sizeof(test) == sizeof(int) * (1 + size) in your hypothetical example, which isn't a compile-time known value!
You can make size into a template parameter; that's about the only solution:
template <unsigned int N>
class Test
{
int array[N];
static const unsigned int size = N; // unnecessary really
public:
// ...
};
Usage: Test<50> x;
Note that now we have sizeof(Test<N>) == sizeof(int) * (1 + N), which is in fact a compile-time known value, because for each N, Test<N> is a distinct type.
You mean a fixed sized array? You could use std::array like this:
#include <array>
class test
{
static const size_t s_size = 50;
std::array<int, s_size> m_array;
public:
test()
{
}
};
Or if you want to support different sizes you need to resort to a class template like this:
#include <array>
template <size_t SIZE>
class test
{
std::array<int, SIZE> m_array;
public:
test()
{
}
};
std:array has the added benefit of keeping the size information along with the member (unlike arrays which decay to pointers) and is compatible with the standard library algorithms.
There is also a version that Boost offers (boost::array) which is similar.
Your code yields an error because compiler needs to know the size of data type of each member. When you write int arr[N] type of member arr is "an array of N integers" where N must be known number in compile time.
One solution is using enum:
class test
{
enum
{
size = 50
};
int arr[size];
public:
test() {}
};
Another is declaring size as static const member of class:
class test
{
static const int size = 50;
int arr[size];
public:
test(){}
};
Note that in-class initialization is allowed only for static class integers! For other types you need to initialize them in code file.