![](/img/trans.png)
[英]Why does std::array require the size as a template parameter and not as a constructor parameter?
[英]Array Size as Constructor Parameter
我正在创建一个包装浮点2d数组并提供一些其他功能的C ++类。 我想将数组的形状作为参数传递给构造函数,请参见下面的代码(类Block部分)。 该行以注释“ // here”结尾,这会在编译期间导致错误,因为当时不知道_nx和_ny。 有两种解决方案(我认为):一种是使用指针(请参见下面的代码中的解决方案1)并动态分配数组;另一种是使用指针。 另一个正在使用模板(请参见下面的代码中的解决方案2),但是我有几个不使用它们的原因:
另外,我不想使用stl向量,因为数组大小在创建后是固定的。 我也在做数值计算,所以“原始”数组更适合我。
我在SO中进行搜索,有五个或六个问题在问类似的问题,虽然没有一个结论是更好的,但都不是从数值的观点出发,因此矢量或new / detele都是它们的好答案-但不是为了我。 我发布此问题的另一个原因是我想知道我在使用c ++功能方面是否过于严格。 正如我将广泛使用c ++一样,了解c ++的局限性并停止对某些不存在的功能进行过多询问/搜索非常重要。
#include <iostream>
#include <memory>
using namespace std;
class Block
{
public:
Block(int nx, int ny):_nx(nx),_ny(ny){}
void Report(void)
{
cout << "Block With Size ["<<_nx<<","<<_ny<<"]\n";
}
private:
const int _nx, _ny;
double _data[_nx][_ny]; // here
};
/// Solution 1, using auto_ptr
class BlockAuto
{
public:
BlockAuto(int nx, int ny):_nx(nx),_ny(ny),_data(new double[_nx*_ny]){}
void Report(void)
{
cout << "BlockAuto With Size ["<<_nx<<","<<_ny<<"]\n";
}
private:
const int _nx;
const int _ny;
const auto_ptr<double> _data;
};
/// Solution 2, using template
template<unsigned int nx, unsigned int ny>
class BlockTpl
{
public:
BlockTpl():_nx(nx),_ny(ny){}
void Report(void)
{
cout << "BlockTpl With Size ["<<_nx<<","<<_ny<<"]\n";
}
private:
const int _nx;
const int _ny;
double _data[nx][ny]; // uncomfortable here, can't use _nx, _ny
};
int main(int argc, const char *argv[])
{
Block b(3,3);
b.Report();
BlockAuto ba(3,3);
ba.Report();
BlockTpl<3,4> bt;
bt.Report();
return 0;
}
只需使用std::vector
。 一周前我遇到了同样的决策问题,并在这里提出了问题 。
如果使用reserve()
,它不会使vector
自身多次重新分配(如果有的话),那么vectors
将不会影响项目的性能。 换句话说, vector
不太可能成为您的瓶颈。
注意,在C++
vectors
已被广泛使用,因此在release mode
,对其进行的优化确实非常有效。
或等待引入std::dynarray
! (不幸的是,不是在C++14
,而是在array TS
或C++17
)。 来源 ,归功于manlio。
永远不要忘记: 过早的优化是邪恶的源头。 -Knuth。
不相信我吗? 你不应该! 尝试一下并找出答案!
这是我的实验,目的是让我在与您有完全相同的问题时深信不疑。
实验代码:
#include <iostream>
#include <vector>
#include <ctime>
#include <ratio>
#include <chrono>
using namespace std;
int main() {
const int N = 100000;
cout << "Creating, filling and accessing an array of " << N << " elements.\n";
using namespace std::chrono;
high_resolution_clock::time_point t1 = high_resolution_clock::now();
int array[N];
for(int i = 0; i < N; ++i)
array[i] = i;
for(int i = 0; i < N; ++i)
array[i] += 5;
high_resolution_clock::time_point t2 = high_resolution_clock::now();
duration<double> time_span = duration_cast<duration<double>>(t2 - t1);
std::cout << "It took me " << time_span.count() << " seconds.";
std::cout << std::endl;
cout << "Creating, filling and accessing an vector of " << N << " elements.\n";
t1 = high_resolution_clock::now();
vector<int> v;
v.reserve(N);
for(int i = 0; i < N; ++i)
v.emplace_back(i);
for(int i = 0; i < N; ++i)
v[i] += 5;
t2 = high_resolution_clock::now();
time_span = duration_cast<duration<double>>(t2 - t1);
std::cout << "It took me " << time_span.count() << " seconds.";
std::cout << std::endl;
return 0;
}
结果(注意-o2
编译器标志):
samaras@samaras-A15:~$ g++ -std=gnu++0x -o2 px.cpp
samaras@samaras-A15:~$ ./a.out
Creating, filling and accessing an array of 100000 elements.
It took me 0.002978 seconds.
Creating, filling and accessing an vector of 100000 elements.
It took me 0.002264 seconds.
因此,只是一个std::vector
。 :)我很确定您知道如何更改代码,并且不需要我告诉您(是的,当然,请告诉我:))。
您可以尝试在我的伪站点中找到的其他时间方法。
如今,内存很便宜,您的块矩阵非常小。
因此,当您不想使用模板并且不想使用动态分配时,只需使用足够大的固定大小的数组来容纳最大的块。
就这么简单。
您使用std::auto_ptr
显示的代码有两个主要问题:
在C ++ 11中不推荐使用std::auto_ptr
。
std::auto_ptr
始终执行delete p
,当分配是数组时,如new T[n]
,将产生Undefined Behavior。
顺便说一下,对于预想的带有模板的代码膨胀,如果进行测量 ,您可能会感到惊喜。
顺便说一句,这闻起来还有些过早的优化。 使用C ++时,最好始终牢记性能,而不要不必要地进行缓慢或占用内存的操作。 但是,这也是一个好主意,不要因不必要地解决一些实际无关紧要的性能问题而陷入困境,或者如果只是忽略它就不会有问题。
因此,您的主要默认选择应该是使用std::vector
进行存储。
然后,如果您怀疑它太慢,请测量 。 发行版本。 哦,我只说了两次,所以这里是第三个: 测量 。 ;-)
std :: vector是您的朋友,无需重建轮子:
class Block
{
public:
BlockAuto(int p_rows, int p_cols):m_rows(nx),m_cols(ny)
{
m_vector.resize(m_rows*m_cols);
}
double at(uint p_x, uint p_y)
{
//Some check that p_x and p_y aren't over limit is advised
return m_vector[p_x + p_y*m_rows];
}
void Report(void)
{
cout << "Block With Size ["<<_nx<<","<<_ny<<"]\n";
}
private:
const int m_rows;
const int m_cols;
std::vector<double> m_vector;
//or double* m_data
};
您也可以在第一个解决方案中使用简单的double *。 但是,销毁该块时,请不要忘记删除它。
我认为您只是因为可调整大小性问题而对std::vector
过于谨慎。 当然,您的程序可以容纳的sizeof(Block)
比原始指针解决方案大几个指针。 如果使用单个vector
而不是vector
,那么就性能而言,用于维护矩阵的vector
应与指针解决方案没有区别。
使用vector
也会使您搞砸的可能性大大降低。 例如,您的auto_ptr
解决方案具有未定义的行为,因为auto_ptr
将要delete
而不是析构函数中的数组delete[]
。 另外,除非您定义复制构造函数和赋值运算符,否则您极有可能不会获得预期的行为。
现在,如果您必须避免使用vector
,我建议您使用unique_ptr
而不是auto_ptr
。
class Block
{
public:
Block(int nx, int ny):_nx(nx),_ny(ny), _data(new double[_nx*_ny])
{}
void Report(void)
{
cout << "Block With Size ["<<_nx<<","<<_ny<<"]\n";
}
private:
const int _nx, _ny;
std::unique_ptr<double[]> _data; // here
};
这将正确调用delete[]
,并且不会像auto_ptr
那样容易地转移数组的所有权。
声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.