我正在尝试编写自己的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::vector
的std::initializer_list
构造函数来创建行x12D向量,其中每一行向量都将其唯一元素初始化为columns
,即std::size_t
,这将导致从std::size_t
到float
的范围缩小警告。它使用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();
}