carloselfietro
carloselfietro

Reputation: 51

C++: using initializer list to do a narrowing conversion

C++11, using Visual Studio 2019.

I have a class Vector2D with 2 floats: x and y. And I am using a legacy class RECT with 4 longs: left, top, right, bottom.

There is a function which requires a Vector2D as argument:

void DrawLine(Vector2D v1, Vector2D v2);

I want the following code to compile without giving me the narrowing conversion error (C2398):

struct RECT
{
    LONG left;
    LONG top;
    LONG right;
    LONG bottom;
};

struct Vector2D
{
    float x;
    float y;
};
    
void DrawLine(Vector2D v1, Vector2D v2)
{
    // stuff
}
    
...
    
void main(){
    RECT r = { 10, 10, 100, 100 };
    DrawLine({r.left, r.top}, {r.right, r.bottom});
}

I have tried adding constructors with a std::initializer_list<long> to Vector2D, but I still get the C2398 error. Referring to a similar question, initializer lists don't allow narrowing this way.

Any other methods I could try? Losing precision, etc is not an issue for me. I can do it with:

DrawLine({static_cast<float>(r.left), static_cast<float>(r.top)}, {static_cast<float>(r.right), static_cast<float>(r.bottom)});

But I am way too lazy to keep on doing this every time, and it clutters up the code.

Upvotes: 0

Views: 278

Answers (3)

carloselfietro
carloselfietro

Reputation: 51

Thanks to an idea from HolyBlackCat I am using the following contructor in Vector2D:

template<typename T, typename U>
Vector2D(const T& tx, const U& ty)
{
    x = static_cast<decltype(x)>(tx);
    y = static_cast<decltype(y)>(ty);
}

I know it is not the best practice regarding implicit narrowing etc, but it lets the compiler cast from all arithmetic types in only a few lines of code.

Upvotes: 1

Remy Lebeau
Remy Lebeau

Reputation: 596497

As @Blindly mentioned in a comment, simply give Vector2D() a constructor that takes 2 LONGs as input, eg:

struct Vector2D
{
    float x;
    float y;

    Vector2D(float x, float y) : x(x), y(y) {}
    Vector2D(LONG x, LONG y) : x(static_cast<float>(x)), y(static_cast<float>(y)) {}
};

Then your call to DrawLine({r.left, r.top}, {r.right, r.bottom}) will work as expected.

Demo

Upvotes: 1

Blindy
Blindy

Reputation: 67378

But I am way too lazy to keep on doing this every time

Well what you are doing is implicit narrowing, and losing precision, so without telling the compiler you're okay with it explicitly (through casting) will generate a warning (that you can turn off, but probably shouldn't).

But that's because you're using implicit construction. Nothing is stopping you from adding proper constructors to your vector class, one that takes floats and one that takes longs, and have the explicit conversion in the latter constructor done only once.

Upvotes: 0

Related Questions