Search code examples
c++vectorwarningsimplicit-conversionemplace

How to detect implicit conversion losses integer precision on std::vector::emplace_back


I compiled the following code with -Wconversion compiler option to detect implicit conversion loses integer precision:

#include <vector>
#include <cstdint>

int main() {
    std::vector<std::uint16_t> v;
    std::uint32_t a = 0;
    v.emplace_back(a);  // no warning
    v.push_back(a);     // warning: implicit conversion loses integer precision
}

Compiling Demo https://wandbox.org/permlink/K5E4sUlfGBw6C5w8

The vector's value_type is std::uint16_t. If I push_back std::uint32_t value to the vector, then I got the following warning as I expected.

prog.cc:8:17: warning: implicit conversion loses integer precision: 'std::uint32_t' (aka 'unsigned int') to 'std::__1::vector<unsigned short, std::__1::allocator<unsigned short> >::value_type' (aka 'unsigned short') [-Wimplicit-int-conversion]
v.push_back(a);     // warning: implicit conversion loses integer precision
  ~~~~~~~~~ ^
1 warning generated.

However, if I emplace_back the same value to the vector, no warning is detected.

I've tested it clang++ 10.0.0, clang++ 9.0.0, and g++ 9.3.0 and got the same result.

Is there any good way to detect implicit conversion losses integer precision on std::vector::emplace_back ?


Solution

  • There is no implicit conversion when you call v.emplace_back(a). There would be an implicit conversion if you called v.emplace_back<const std::uint16_t &>(a).

    A key difference between push_back and emplace_back is that the latter is a template while the former is not. If you do not specify a template argument for emplace_back, the compiler deduces it from the function argument, which means no conversion will be necessary at the point of call. Within emplace_back the conversion happens in a system header, which suppresses the warning.

    So in your example,

    v.emplace_back(a);
    

    is deduced as

    v.emplace_back<const std::uint32_t &>(a);
    

    where the function argument is expected to be a std::uint32_t. Perfect match, no conversion necessary outside the system header. If you were to enable warnings within system headers, you could end up with a bunch of spurious warnings

    To get an implicit conversion in your code, you need to force emplace_back to expect a std::uint16_t, which can be done via

    v.emplace_back<const std::uint16_t &>(a);
    

    This would implicitly convert a to std::uint16_t before calling emplace_back, triggering the compiler warning in the same way that push_back does.