GCC, clang disagree with MSVC on narrowing conversion

Viewed 104

Consider the following program:

struct uint1 {
    unsigned x;
    uint1(unsigned x_) : x(x_) { }
};

struct foo { uint1 a; };

foo f(int v) { 
    return {v}; 
}

struct bar { unsigned a; };

bar g(int v) {
    return {v};
}

Now, where do we have narrowing conversions here?

Compiler version narrowing conversion in f()? narrowing conversion in g()?
GCC 11.2 No Yes
clang 13.0 No Yes
MSVC 19.4 Yes Yes

Which is right? Naively, it seems to me there's a narrowing conversion in both functions.

See it all on GodBolt.

Note: C++17 in case it matters.

1 Answers

It seems that GCC and Clang are right in not classing this as a narrowing conversion.

The initialisation in question is that of a foo object. Its constructor argument is enclosed in {}. There cannot be a narrowing conversion in this initialisation, because the argument of the constructor is not of a numeric type. It is struct uint1.

So where does struct uint1 cone from? It is converted from v, but not by means of braced initialisation. The pair of braces around v is already occupied by the constructor of uint1.

So this is equivalent to foo{uint1(v)}.

MSVC on the other hand seems to interpret this as foo(uint1{v}), and I don't think this interpretation is justified.

Neither compiler complains about an explicit foo{uint1(v)}, as is required by the standard, because there is no narrowing at the top level.

Related