Нет сужающих предупреждений при использовании `emplace_back` вместо` push_back` - PullRequest
1 голос
/ 04 мая 2020

У моего коллеги неожиданная проблема с emplace_back, и я пытаюсь обернуть ее вокруг. Следующий test.cpp является минимальным примером, который воспроизводит проблему:

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

Компиляция с g ++ 8.3.0 выдает следующее предупреждение:

$ g++ -Wconversion test.cpp -o test
test.cpp: In function ‘int main()’:
test.cpp:10:10: warning: conversion from ‘double’ to ‘int’ may change value [-Wfloat-conversion]
   A a(bar); // Gives compiler warning with Wconversion as expected

Таким образом, неявное преобразование обнаруживается, когда простой объект создается, но не тогда, когда вызывается emplace_back.

Почему нет предупреждения для emplace_back?

Ответы [ 2 ]

1 голос
/ 04 мая 2020

Это следствие того, как распределитель по умолчанию создает A. Когда вы делаете A a{foo, bar}, вы используете инициализацию списка, и для выдачи диагноза c требуется сужающее преобразование. Распределитель по умолчанию использует

::new (static_cast<void*>(p)) T(std::forward<Args>(args)...)

, где p - указатель на элемент векторных данных, а T - value_type вектора. Здесь они используют скобки вместо скобок, а с скобками разрешены сужающие преобразования, поэтому вы не увидите сообщение о диагностике c.

Если вы написали свой собственный распределитель, который выполнил

::new (static_cast<void*>(p)) T{std::forward<Args>(args)...}

Тогда вы получите предупреждение.

0 голосов
/ 05 мая 2020

Чтобы получить предупреждение, вам нужно скомпилировать:

$ g++ -Wsystem-headers -Wconversion test.cpp -o test
In file included from /usr/include/c++/8/vector:60,
                 from test.cpp:1:
/usr/include/c++/8/bits/stl_algobase.h: In function ‘constexpr int std::__lg(int)’:
/usr/include/c++/8/bits/stl_algobase.h:1001:44: warning: conversion from ‘long unsigned int’ to ‘int’ may change value [-Wconversion]
   { return sizeof(int) * __CHAR_BIT__  - 1 - __builtin_clz(__n); }
                                            ^
/usr/include/c++/8/bits/stl_algobase.h: In function ‘constexpr unsigned int std::__lg(unsigned int)’:
/usr/include/c++/8/bits/stl_algobase.h:1005:44: warning: conversion from ‘long unsigned int’ to ‘unsigned int’ may change value [-Wconversion]
   { return sizeof(int) * __CHAR_BIT__  - 1 - __builtin_clz(__n); }
                                            ^
In file included from /usr/include/x86_64-linux-gnu/c++/8/bits/c++allocator.h:33,
                 from /usr/include/c++/8/bits/allocator.h:46,
                 from /usr/include/c++/8/vector:61,
                 from test.cpp:1:
/usr/include/c++/8/ext/new_allocator.h: In instantiation of ‘void __gnu_cxx::new_allocator<_Tp>::construct(_Up*, _Args&& ...) [with _Up = A; _Args = {double&}; _Tp = A]’:
/usr/include/c++/8/bits/alloc_traits.h:475:4:   required from ‘static void std::allocator_traits<std::allocator<_Tp1> >::construct(std::allocator_traits<std::allocator<_Tp1> >::allocator_type&, _Up*, _Args&& ...) [with _Up = A; _Args = {double&}; _Tp = A; std::allocator_traits<std::allocator<_Tp1> >::allocator_type = std::allocator<A>]’
/usr/include/c++/8/bits/vector.tcc:103:30:   required from ‘void std::vector<_Tp, _Alloc>::emplace_back(_Args&& ...) [with _Args = {double&}; _Tp = A; _Alloc = std::allocator<A>]’
test.cpp:9:25:   required from here
/usr/include/c++/8/ext/new_allocator.h:136:4: warning: conversion from ‘double’ to ‘int’ may change value [-Wfloat-conversion]
  { ::new((void *)__p) _Up(std::forward<_Args>(__args)...); }

Предупреждение больше не так ясно, как это происходит в системном заголовке. Обратите внимание, что недостаточно использовать только флаг -Wsystem-headers, для этого нужно использовать -Wsystem-headers и -Wconversion.

В случае push_back достаточно использовать -Wnarrowing чтобы получить предупреждение.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...