gpt4 book ai didi

c++ - 使用 Curiously Recurring Template Pattern 时如何实例化基类?

转载 作者:塔克拉玛干 更新时间:2023-11-03 07:04:16 27 4
gpt4 key购买 nike

我正在构建自己的 Array 实现,其中包含几个新功能支持和运算符。我 researched a lot 关于扩展 std::array 最后,它导致了很多问题,我决定使用组合而不是继承。

接下来,我们可以看到一小部分使用模板元编程的 Array 自定义实现。在这个简单的版本中,有一个用于 std::ostream 的打印方法和一个简单的 operator/ 定义:

#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;
}

但是,此实现使用 Curiously Recurring Template Pattern 有一个限制。我找不到直接实例化基类 Array 数组的方法。例如,如果我尝试以下操作:

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;
}

编译器说:

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 {
^~~~~

然后,我尝试将自己的模板类作为默认参数传递给 struct Array 声明,如下所示:

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;
// ...

但是,我发现编译器似乎不允许将模板类传递给另一个模板类,因为如果未实例化,它们不会定义类型。

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;

因此,这似乎是一个悖论,因为在事先不知道我的完整定义的情况下,我无法用自己实例化自己。然后,我尝试创建一个名为 ConcreteArray 的虚拟类型,如下所示:

struct ConcreteArray
{
};

template <unsigned int array_width, typename DataType, typename DerivedType=ConcreteArray>
struct Array {
std::array<DataType, array_width> _data;
// ...

但是,这在直接实例化 Array 类时会产生问题,因为作为除法 operator/ 的已实现运算符的返回类型未正确实例化为派生类类(class)类型:

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;
~~~~~~~~~~^~~~~

如何在使用 Curiously Recurring Template Pattern 时实例化基类?

引用资料:

  1. C++ static polymorphism (CRTP) and using typedefs from derived classes
  2. Curiously Recurring Template Pattern and statics in the base class

最佳答案

在某些情况下使用 Array 作为 DerivedType 而在其他情况下使用实际的派生类型是不对称的,正如您在回答中所展示的那样。

我想建议一个使用不同方法的解决方案。对于“派生类型”不存在的情况,它使用“空派生类型”。

#include <iostream>
#include <array>

template <unsigned int array_width, typename DataType>
struct empty_derived_type;

template
<
unsigned int array_width,
typename DataType,
typename DerivedType = empty_derived_type<array_width, DataType>
>
struct Array {
std::array<DataType, array_width> _data;

Array() {
for(unsigned 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;
}
};

template <unsigned int array_width, typename DataType>
struct empty_derived_type : public Array
<
array_width,
DataType,
empty_derived_type<array_width, DataType>
>
{
};

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;

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;
}

输出:

coordinate: (1, 1, 1)
new_coordinate: (0.1, 0.1, 0.1)
int_array: (1, 1, 1, 1, 1)
new_int_array: (0, 0, 0, 0, 0)

关于c++ - 使用 Curiously Recurring Template Pattern 时如何实例化基类?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/53463049/

27 4 0
Copyright 2021 - 2024 cfsdn All Rights Reserved 蜀ICP备2022000587号
广告合作:1813099741@qq.com 6ren.com