I defined a custom array implementation with template meta programming as follows:
#include <array>
#include <iostream>
template <unsigned int array_width, typename DataType, typename DerivedType>
struct Array {
std::array<DataType, array_width> _data;
Array() {
for(int index = 0; index < array_width; ++index) _data[index] = 1;
}
DerivedType operator/(const double& data) {
unsigned int column;
DerivedType new_array;
for(column = 0; column < array_width; column++) {
new_array._data[column] = _data[column] / data;
}
return new_array;
}
friend std::ostream& operator<<( std::ostream &output, const Array &array ) {
unsigned int column; output << "(";
for( column=0; column < array_width; column++ ) {
output << array._data[column];
if( column != array_width-1 ) {
output << ", ";
}
}
output << ")"; return output;
}
};
struct Coordinate : public Array<3, double, Coordinate> {
typedef Array< 3, double, Coordinate > SuperClass;
double& x;
double& y;
double& z;
Coordinate() : SuperClass{}, x{this->_data[0]}, y{this->_data[1]}, z{this->_data[2]} {}
};
int main() {
Coordinate coordinate;
std::cout << "coordinate: " << coordinate << std::endl;
Coordinate new_coordinate = coordinate / 10.0;
std::cout << "new_coordinate: " << new_coordinate << std::endl;
}
But I cannot find a way to directly instantiate an array of the base class Array
. For example, if I try the following:
int main() {
Array<5, int> int_array;
std::cout << "int_array: " << int_array << std::endl;
Array<5, int> new_int_array = int_array / 10;
std::cout << "new_int_array: " << new_int_array << std::endl;
}
The compiler says:
test.cpp: In function 'int main()':
test.cpp:45:15: error: wrong number of template arguments (2, should be 3)
Array<5, int> int_array;
^
test.cpp:6:8: note: provided for 'template<unsigned int array_width, class DataType, class DerivedType> struct Array'
struct Array {
^~~~~
test.cpp:48:15: error: wrong number of template arguments (2, should be 3)
Array<5, int> new_int_array = int_array / 10;
^
test.cpp:6:8: note: provided for 'template<unsigned int array_width, class DataType, class DerivedType> struct Array'
struct Array {
^~~~~
Then, I tried to pass the own template class as a default argument for the struct Array
declaration as follows:
template <unsigned int array_width, typename DataType, typename DerivedType>
struct Array;
template <unsigned int array_width, typename DataType, typename DerivedType=Array>
struct Array {
std::array<DataType, array_width> _data;
// ...
However, I figured out the compiler seems to not allow to pass template classes to another template class, because they do not define a type if the are not instantiated yet.
test.cpp:8:77: error: invalid use of template-name 'Array' without an argument list
template <unsigned int array_width, typename DataType, typename DerivedType=Array>
^~~~~
test.cpp:8:77: note: class template argument deduction is only available with -std=c++1z or -std=gnu++1z
test.cpp:6:8: note: 'template<unsigned int array_width, class DataType, class DerivedType> struct Array' declared here
struct Array;
^~~~~
test.cpp: In function 'int main()':
test.cpp:48:15: error: template argument 3 is invalid
Array<5, int> int_array;
^
test.cpp:51:15: error: template argument 3 is invalid
Array<5, int> new_int_array = int_array / 10;
Hence, it seems a paradox because I cannot instantiate the myself with myself without knowing my complete definition beforehand. Then, I tried to create a dummy type called ConcreteArray
as next:
struct ConcreteArray
{
};
template <unsigned int array_width, typename DataType, typename DerivedType=ConcreteArray>
struct Array {
std::array<DataType, array_width> _data;
// ...
However, this creates serious problems when directly instantiating the Array
class, as the returned by the implemented quotient/division operator is not the correct instance required by the Curiously Recurring Template Pattern:
test.cpp: In function 'int main()':
test.cpp:52:43: error: conversion from 'ConcreteArray' to non-scalar type 'Array<5, int>' requested
Array<5, int> new_int_array = int_array / 10;
~~~~~~~~~~^~~~
test.cpp: In instantiation of 'DerivedType Array<array_width, DataType, DerivedType>::operator/(const double&) [with unsigned int array_width = 5; DataType = int; DerivedType = ConcreteArray]':
test.cpp:52:45: required from here
test.cpp:22:17: error: 'struct ConcreteArray' has no member named '_data'
new_array._data[column] = _data[column] / data;
~~~~~~~~~~^~~~~
References:
- C++ static polymorphism (CRTP) and using typedefs from derived classes
- Curiously Recurring Template Pattern and statics in the base class
- Practical Uses for the "Curiously Recurring Template Pattern"
Aucun commentaire:
Enregistrer un commentaire