自定义二维向量类的适当大小初始化



我正在尝试编写自己的Vector2D类,当我尝试在构造函数中初始化Vector2D的大小时遇到了问题。

#include <vector>
template <typename T>
class Vector2D
{
public:
explicit Vector2D(const size_t& rows, const size_t& columns)
: m_data(rows, std::vector<T>{ columns })
{}
private:
std::vector<std::vector<T>> m_data;
};
int main()
{
// Compiles fine
std::vector<std::vector<float>> v1{ 10, std::vector<float>{ 10 }};
// Results in narrowing conversion warnings!
Vector2D<float> v2{ 10, 10 };
}

main的第一行中,我可以容易地初始化二维向量并设置两个维度的大小。

在下一行中,我试图通过Vector2D的构造函数做完全相同的事情,但由于某种原因,我收到了关于缩小转换的警告!

<source>: In instantiation of 'Vector2D<T>::Vector2D(const size_t&, const size_t&) [with T = float; size_t = long unsigned int]':
<source>:21:32:   required from here
<source>:8:49: warning: narrowing conversion of '(size_t)columns' from 'size_t' {aka 'long unsigned int'} to 'float' [-Wnarrowing]
8 |         : m_data(rows, std::vector<T>{ columns })
|                                                 ^
<source>:8:49: warning: narrowing conversion of 'columns' from 'const size_t' {aka 'const long unsigned int'} to 'float' [-Wnarrowing]

既然Vector2D构造函数本质上与编译良好的行做的事情完全相同,为什么它会导致编译器警告?

警告告诉了你需要知道的一切,你选择忽略它。这是你的构造函数。

explicit Vector2D(const size_t& rows, const size_t& columns)
: m_data(rows, std::vector<T>{ columns })
{}

初始化部分使用std::vectorstd::initializer_list构造函数来创建行x12D向量,其中每一行向量都将其唯一元素初始化为columns,即std::size_t,这将导致从std::size_tfloat的范围缩小警告。它使用std::initializer_list构造函数,因为您使用的是braked初始化。就这么简单。

修复方法是通过在初始化部分的构造函数调用中将支持的初始化{}更改为()来简单地调用正确的构造函数。

至于为什么在main()中声明标准2D矢量时没有出现错误。原因是一样的。测试它就像打印尺寸一样简单。

#include <vector>
#include <iostream>
int main()
{
// Compiles fine
std::vector<std::vector<float>> v1{ 10, std::vector<float>{ 10 }};
for (auto vec : v1) {
std::cout << vec.size() << 'n';
}

此打印:

1
1
1
1
1
1
1
1
1
1

您创建了一个10x1二维矢量,因为您传递了一个std::initializer_list。这就是为什么它很可能没有按照你想象的那样做。字面上的10也是一个int,将其转换为float不会发出警告,因为这两种类型在你的系统上可能大小相同,而且你明确地在其中放置了10,这会使缩小范围的警告静音。测试完全相同的逻辑会导致完全相同的错误,如下所示:

#include <vector>
int main()
{
std::size_t val = 10;
std::vector<std::vector<float>> v1{ 10, std::vector<float>{ val }};
}

如果您尝试将std::size_t(10)放在val的位置,它会使警告静音,但您仍然缩小了数据范围。编译器只是假设您现在想这么做,因为它是显式完成的。

这是您的代码,已修复:

#include <vector>
#include <iostream>
template <typename T>
class Vector2D
{
public:
explicit Vector2D(const size_t& rows, const size_t& columns)
: m_data(rows, std::vector<T>(columns))  // Only changed {} to ()
{}
// private:  // For ease of testing
std::vector<std::vector<T>> m_data;
};
int main()
{
Vector2D<float> v2{ 10, 10 };
for (auto vec : v2.m_data) {
std::cout << vec.size() << 'n';
}
}

由于您的类不提供std::initializer_list构造函数,因此您不必为Vec2D担心它。但std::vector确实如此(在列表中排名第10(,并且您一直在调用它们,然后坚称自己不是。与另一个答案不同,我选择不在Vec2D构造函数中提供默认值,这意味着Vec2D对象将使用值T()进行实例化,如果T是DefaultConstructable,则该值将始终有效。对于任何不能用值0初始化的类,另一个答案会立即中断,该值不是一个小数字。

在未来,如果你听别人告诉你的,而不是推诿,对所有参与其中的人来说都会容易得多。你是那个代码被破坏的人,这个网站上没有人欠你任何东西。

这个警告实际上非常有用,如果你打印由此产生的矢量,你会看到这个

#include <iostream>
#include <vector>
template <typename T> class Vector2D {
public:
explicit Vector2D(const size_t &rows, const size_t &columns)
: m_data(rows, std::vector<T>{columns, 0}) {}
void print() {
for (auto vec : m_data) {
for (auto val : vec)
std::cout << val << ' ';
std::cout << ',';
}
}
private:
std::vector<std::vector<T>> m_data;
};
int main() {
Vector2D<float> v2{10, 10};
v2.print();
}

/a.out

10 0
10 0
10 0
10 0
10 0
10 0
10 0
10 0
10 0
10 0

这是由于向量的initializer_list构造函数从size_t到float的转换范围缩小所致

要解决此问题,您需要将大括号{}替换为括号((,如所示

#include <iostream>
#include <vector>
template <typename T> class Vector2D {
public:
explicit Vector2D(const size_t &rows, const size_t &columns)
: m_data(rows, std::vector<T>(columns)) {}
private:
std::vector<std::vector<T>> m_data;
};
int main() {
Vector2D<float> v2{10, 10};
v2.print();
}

最新更新