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

Proper size initialization of a custom two dimensional vector class

我正在尝试编写自己的 Vector2D class,但当我尝试初始化 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 构造函数基本上与编译良好的行完全相同,为什么会导致编译器警告?

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

    #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

这是由于从 size_t 到 float 的缩小转换导致的 initializer_list 构造函数 vector

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

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

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

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

初始化部分使用 std::vectorstd::initializer_list 构造函数创建行 x 1 二维向量,其中每个行向量都有其唯一元素初始化为 columns,这是一个 std::size_t,这导致从 std::size_tfloat 的缩小警告。它使用 std::initializer_list 构造函数,因为您使用的是花括号初始化。就这么简单。

解决方法是通过在初始化部分的构造函数调用中将大括号初始化 {} 更改为 () 来简单地调用正确的构造函数。

关于为什么在 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 放在那里,这会消除缩小警告.测试完全相同的逻辑会导致完全相同的错误,如 here:

所示
#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';
    }
}

由于您的 class 没有提供 std::initializer_list 构造函数,因此您不必为 Vec2D 担心。 But std::vector does(#10 in the list), and you kept invoking them, and then insisting that you weren't. Unlike the other answer, I am opting to not provide a default value in your Vec2D constructor, meaning that the Vec2D object will instantiated with values of T(), which will always work if T is DefaultConstructable。对于任何不能用 0 值初始化的 class,另一个答案立即中断,这不是一个小数字。

在未来,如果您倾听别人告诉您的内容而不是顶撞,那么对于所有相关人员来说都会容易得多。你是那个破坏代码的人,这个网站上没有人欠你任何东西。