The warning told you everything you needed to know, you were choosing to ignore it. Here's your constructor.
explicit Vector2D(const size_t& rows, const size_t& columns)
: m_data(rows, std::vector<T>{ columns })
{}
The initialization section is using the std::initializer_list
constructor of std::vector
to create a rows x 1 2D vector, where every row vector gets its sole element initialized to columns
, which is a std::size_t
, which results in a narrowing warning from std::size_t
to float
. It's using the std::initializer_list
constructor because you are using braced initialization. It's as simple as that.
The fix is to simply invoke the correct constructor by changing your braced initialization {}
to ()
in the constructor call in the initialization section.
As to why you're not getting an error when you declared a standard 2D vector in your main()
. It's the same reason. Testing it is as simple as printing the dimensions.
#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';
}
This prints:
1
1
1
1
1
1
1
1
1
1
You created a 10x1 2D vector because you passed a std::initializer_list
. Which is why it's very likely not doing what you think it is. The literal 10
is also an int, and converting it to float won't throw the warning because the two types are likely the same size on your system, and you're explicitly placing 10 in there, which silences narrowing warnings. Testing the exact same logic would have resulted in the exact same error, as seen here:
#include <vector>
int main()
{
std::size_t val = 10;
std::vector<std::vector<float>> v1{ 10, std::vector<float>{ val }};
}
If you try to place std::size_t(10)
in place of val
, it silences the warning, but you've still narrowed your data. The compiler simply assumes you meant to now because it was explicitly done.
Here is your code, fixed up:
#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';
}
}
Since your class provides no std::initializer_list
constructors, you don't have to worry about it for 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. The other answer immediately breaks for any class that cannot be initialized with a value of 0, which is not a small number.
In the future, it would be a lot easier for everyone involved if you listened to what you're being told instead of pushing back. You're the one with the broken code and no one on this site owes you anything.