lundi 4 mai 2020

No narrowing warnings when using `emplace_back` instead of `push_back`

My colleague ran into an unexpected issue with emplace_back and I am trying to wrap my head around it. The following test.cpp is a minimal example that reproduces the issue:

#include <vector>                                                                                                                                                                                                  
class A {
 public:
  A(int /*unused*/, int /*unused*/) {}
};
int main() {
  int foo = 5;
  double bar = 4.5;
  std::vector<A> a_vec{};
  a_vec.emplace_back(foo, bar); // Not caught by either clang-tidy or gcc
  A a{foo, bar}; // Gives compiler warning as expected
}

Compiling with g++ 8.3.0 yields the following warning:

$ g++ -std=c++11 test.cpp -o test
test.cpp: In function ‘int main()’:
test.cpp:10:15: warning: narrowing conversion of ‘bar’ from ‘double’ to ‘int’ inside { } [-Wnarrowing]
   A a{foo, bar}; // Gives compiler warning as expected

So the implicit conversion is caught when a simple object is constructed, but not when emplace_back is called. Even when the keyword explicit is added to the constructor, no warning is generated.

Is it possible in some way to get conversion warnings when using emplace_back like this?

Changing the emplace_back line to: a_vec.emplace_back(A(foo, bar)) does not generate a warning (even with the explicit keyword). The only way to get a warning seems to be using push_back or calling the constructor outside/before the emplace_back call:

#include <vector>                                                                                                                                                                                                  
class A {
 public:
  A(int /*unused*/, int /*unused*/) {}
};
int main() {
  int foo = 5;
  double bar = 4.5;
  std::vector<A> a_vec{};
  A a{foo, bar}; // Gives compiler warning as expected
  a_vec.emplace_back(a);
}

I have tried several compiler flags, e.g. -Wall -Wextra -Wpedantic -Werror -Wsystem-headers, but none seem to help.

Looking at the documentation for emplace_back did not directly help. The reason could be related to the usage of std::allocator_traits::construct or std::forward, but I am uncertain on how.

I mostly want to know if it is possible to get the warnings when calling emplace_back.

Aucun commentaire:

Enregistrer un commentaire