No narrowing warnings when using `emplace_back` instead of `push_back` - c++

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:
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
}
Compiling with g++ 8.3.0 yields the following warning:
$ 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
So the implicit conversion is caught when a simple object is constructed, but not when emplace_back is called.
Why is there no warning for emplace_back?

This is a consequence of how the default allocator constructs an A. When you do A a{foo, bar} you are using list initialization and a narrowing conversion is required to issue a diagnostic. With the default allocator, it uses
::new (static_cast<void*>(p)) T(std::forward<Args>(args)...)
where p is a pointer to the element of the vector data and T is the value_type of the vector. Here they use parentheses instead of braces and with parentheses narrowing conversions are allowed so you don't see a diagnostic message.
If you wrote your own allocator that did
::new (static_cast<void*>(p)) T{std::forward<Args>(args)...}
Then you would get the warning.

In order to get the warning you need to compile with:
$ 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)...); }
The warning is no longer as clear, since it happens in a system header. Note that it is not enough to only use the -Wsystem-headers flag, one needs both -Wsystem-headers and -Wconversion to catch this.
In the case of push_back it is enough to use -Wnarrowing to get a warning.

Related

using the correct to_chars overload

I'm using Clang 14 (on Apple M1), which has full support for C++ 17, and I'm trying to utilize the new to_chars function. Here's my very simple test file:
#include <charconv>
#include <iostream>
int main() {
char a[10];
double pi = 3.141592;
std::to_chars_result res = std::to_chars(a, a+10, pi);
*res.ptr = '\0';
std::cout << a << std::endl;
}
My compile command is clang -std=c++17 test_to_chars.cpp, and the output is below:
test_to_chars.cpp:8:30: error: call to deleted function 'to_chars'
std::to_chars_result res = std::to_chars(a, a+10, pi);
^~~~~~~~~~~~~
/Applications/Xcode.app/Contents/Developer/Platforms/MacOSX.platform/Developer/SDKs/MacOSX.sdk/usr/include/c++/v1/charconv:166:6: note: candidate function has been explicitly deleted
void to_chars(char*, char*, bool, int = 10) = delete;
^
/Applications/Xcode.app/Contents/Developer/Platforms/MacOSX.platform/Developer/SDKs/MacOSX.sdk/usr/include/c++/v1/charconv:450:1: note: candidate template ignored: requirement 'is_integral<double>::value' was not satisfied [with _Tp = double]
to_chars(char* __first, char* __last, _Tp __value)
^
/Applications/Xcode.app/Contents/Developer/Platforms/MacOSX.platform/Developer/SDKs/MacOSX.sdk/usr/include/c++/v1/charconv:458:1: note: candidate function template not viable: requires 4 arguments, but 3 were provided
to_chars(char* __first, char* __last, _Tp __value, int __base)
^
test_to_chars.cpp:8:24: error: no viable conversion from 'void' to 'std::to_chars_result'
std::to_chars_result res = std::to_chars(a, a+10, pi);
^ ~~~~~~~~~~~~~~~~~~~~~~~~~~
/Applications/Xcode.app/Contents/Developer/Platforms/MacOSX.platform/Developer/SDKs/MacOSX.sdk/usr/include/c++/v1/charconv:154:25: note: candidate constructor (the implicit copy constructor) not viable: cannot convert argument of incomplete type 'void' to 'const std::to_chars_result &' for 1st argument
struct _LIBCPP_TYPE_VIS to_chars_result
^
/Applications/Xcode.app/Contents/Developer/Platforms/MacOSX.platform/Developer/SDKs/MacOSX.sdk/usr/include/c++/v1/charconv:154:25: note: candidate constructor (the implicit move constructor) not viable: cannot convert argument of incomplete type 'void' to 'std::to_chars_result &&' for 1st argument
2 errors generated.
I'm calling to_chars(char*, char*, double) but for some reason it's using an implicit conversion and trying to call to_chars(char*, char*, bool, int = 10) instead, which is a deleted function.
Is there a way for me to tell C++ that I don't want it to convert my double parameter to a bool?
I'm using Clang 14 (on Apple M1), which has full support for C++ 17
This is unfortunately not correct. While the compiler itself has full C++17 support, the stdlib of your clang version (Apple clang 14) does not implement any floating point charconv features.
See the entry "Elementary string conversions" in the cppreference table.
It is important to note that you are not running "clang 14", but "Apple clang 14". Your code snippet compiles just fine on normal clang 14.

C++ map invalid conversion from int to const LexType& (which is defined by myself in fact a int)

I defined a map like this :
std::map<std::string,LexType> lexname_s = { { "PROGRAM" , PROGRAM}}
And a LexType, like this :
typedef enum
{
ENDFILE, ERROR,
PROGRAM, PROCEDURE, TYPE, VAR, IF,
} LexType;
In Visual Studio Code, it always shows error type when I touch it.
//
I add more details for what i said.
the line
std::map<std::string,LexType> lexname_s = { { "PROGRAM" , PROGRAM}}
show error . it seems i can't initialize it in this way.
I compile it in the gcc version 4.8.5 20150623 (Red Hat 4.8.5-39) (GCC) in a cloud server which is centos7.
AND the error code shows below
from parse.cpp:1:
../utils.h:52:27: error: invalid conversion from ‘int’ to ‘const LexType&’ [-fpermissive]
{"ERROR", ERROR}};
^
In file included from /usr/include/c++/4.8.2/bits/stl_algobase.h:64:0,
from /usr/include/c++/4.8.2/bits/stl_tree.h:61,
from /usr/include/c++/4.8.2/map:60,
from ../globals.h:6,
from parse.h:4,
from parse.cpp:1:
/usr/include/c++/4.8.2/bits/stl_pair.h:112:26: error: initializing argument 2 of ‘constexpr std::pair<_T1, _T2>::pair(const _T1&, const _T2&) [with _T1 = const std::basic_string<char>; _T2 = LexType]’ [-fpermissive]
_GLIBCXX_CONSTEXPR pair(const _T1& __a, const _T2& __b)```
EOF(which shows in the map define) is a reserved a macro defined in stdio.h
it's the problem of it.
change the name will be ok.

Why does this use of boost::none fail to compile with nvcc?

I'm trying to compile the following code:
#include <boost/optional.hpp>
void foo(boost::optional<unsigned> x = boost::none);
placed in the file a.cu, with the CUDA compiler, using the following command line:
nvcc a.cu -c --std=c++11 -I/opt/boost/include
but I get a bunch of errors:
a.cu:2:53: error: conversion from ‘const boost::none_t(boost::none_t::init_tag (*)())’ to ‘boost::optional<unsigned int>’ is ambiguous
void foo(boost::optional<unsigned> x = boost::none);
^
/opt/boost/include/boost/optional/optional.hpp:805:1: note: candidate: boost::optional<T>::optional(boost::optional<T>::rval_reference_type) [with T = unsigned int; boost::optional<T>::rval_reference_type = unsigned int&&] <near match>
optional ( rval_reference_type val ) : base( boost::forward<T>(val) )
^ ~~~~
/opt/boost/include/boost/optional/optional.hpp:805:1: note: conversion of argument 1 would be ill-formed:
a.cu:2:53: error: invalid conversion from ‘const boost::none_t (*)(boost::none_t::init_tag (*)())’ to ‘unsigned int’ [-fpermissive]
void foo(boost::optional<unsigned> x = boost::none);
^
/opt/boost/include/boost/optional/optional.hpp:800:1: note: candidate: boost::optional<T>::optional(boost::optional<T>::argument_type) [with T = unsigned int; boost::optional<T>::argument_type = const unsigned int&] <near match>
optional ( argument_type val ) : base(val) {}
^ ~~~~
/opt/boost/include/boost/optional/optional.hpp:800:1: note: conversion of argument 1 would be ill-formed:
a.cu:2:53: error: invalid conversion from ‘const boost::none_t (*)(boost::none_t::init_tag (*)())’ to ‘unsigned int’ [-fpermissive]
void foo(boost::optional<unsigned> x = boost::none);
Why does this happen, and can I circumvent the problem while still actually using boost::optional in (host-side) code compiled with nvcc?
Additional information:
The code compiles fine with g++ 6.3.0 (my distribution's compiler).
This code (or rather, similar code) used to compile and work on an earlier Linux distribution I was using, where the compiler was g++ 5.4.x .
I've tried this with Boost versions 1.65.1 and 1.69.0 .
I've tried this with CUDA versions 9.2.88 and 10.0.130 .
I had the exact same error and was able to get this to work with this modification:
#define BOOST_OPTIONAL_USE_OLD_DEFINITION_OF_NONE
#include <boost/optional.hpp>
This is using CUDA 10.0.130, g++ 7.3.0, and Boost 1.68.0.
A partial answer to the second question:
You could consider using Andrzej Krzemieński's neat and self-contained implementation of an optional instead of boost::optional. It works with C++11, which is what you seem to be doing.

Issue compiling C++11's <random> with gcc 4.8.1

I'm trying to compile some code utilizing C++11's library on a linux server. The code compiles fine on my mac with this line in my makefile:
CC = clang++ -std=c++11 -stdlib=libc++
However, when I try and compile the same code on a Linux server, with gcc version 4.8.1, I get the following error:
$ /usr/local/share/gcc481/bin/g++ -std=c++11 server.cpp
In file included from /auto/pkg/gcc-4.8.1/share/gcc481/include/c++/4.8.1/random:50:0,
from ellipse-graph.h:6,
from server.cpp:1:
/auto/pkg/gcc-4.8.1/share/gcc481/include/c++/4.8.1/bits/random.h: In instantiation of 'class std::uniform_int_distribution<double>':
server.cpp:17:59: required from here
/auto/pkg/gcc-4.8.1/share/gcc481/include/c++/4.8.1/bits/random.h:1668:7: error: static assertion failed: template argument not an integral type
static_assert(std::is_integral<_IntType>::value,
^
In file included from /auto/pkg/gcc-4.8.1/share/gcc481/include/c++/4.8.1/bits/move.h:57:0,
from /auto/pkg/gcc-4.8.1/share/gcc481/include/c++/4.8.1/bits/stl_pair.h:59,
from /auto/pkg/gcc-4.8.1/share/gcc481/include/c++/4.8.1/bits/stl_algobase.h:64,
from /auto/pkg/gcc-4.8.1/share/gcc481/include/c++/4.8.1/vector:60,
from interval.h:4,
from vertex.h:4,
from ellipse-graph.h:4,
from server.cpp:1:
/auto/pkg/gcc-4.8.1/share/gcc481/include/c++/4.8.1/type_traits: In instantiation of 'struct std::make_unsigned<double>':
/auto/pkg/gcc-4.8.1/share/gcc481/include/c++/4.8.1/bits/random.tcc:884:57: required from 'std::uniform_int_distribution<_IntType>::result_type std::uniform_int_distribution<_IntType>::operator()(_UniformRandomNumberGenerator&, const std::uniform_int_distribution<_IntType>::param_type&) [with _UniformRandomNumberGenerator = std::linear_congruential_engine<long unsigned int, 16807ul, 0ul, 2147483647ul>; _IntType = double; std::uniform_int_distribution<_IntType>::result_type = double]'
/auto/pkg/gcc-4.8.1/share/gcc481/include/c++/4.8.1/bits/random.h:1770:51: required from 'std::uniform_int_distribution<_IntType>::result_type std::uniform_int_distribution<_IntType>::operator()(_UniformRandomNumberGenerator&) [with _UniformRandomNumberGenerator = std::linear_congruential_engine<long unsigned int, 16807ul, 0ul, 2147483647ul>; _IntType = double; std::uniform_int_distribution<_IntType>::result_type = double]'
server.cpp:20:40: required from here
/auto/pkg/gcc-4.8.1/share/gcc481/include/c++/4.8.1/type_traits:1531:62: error: invalid use of incomplete type 'class std::__make_unsigned_selector<double, false, false>'
{ typedef typename __make_unsigned_selector<_Tp>::__type type; };
^
/auto/pkg/gcc-4.8.1/share/gcc481/include/c++/4.8.1/type_traits:1495:11: error: declaration of 'class std::__make_unsigned_selector<double, false, false>'
class __make_unsigned_selector;
^
It appears the issue is the header. When I look here https://gcc.gnu.org/onlinedocs/libstdc++/manual/status.html#status.iso.200x, at "26.5", it appears that gcc 4.8.1 doesn't support random number generation, unless I'm interpreting this incorrectly. How might I compile this file?

what is the reason behind vector's push_back error

I have a simple c++ std::vector and inside it, i am storing threads as shown below. Can you please explain why the line with comment "does not compile" shows error during compilation? And why the line with comment "compiles" work?
#include<thread>
#include<vector>
using namespace std;
void abc() {}
int main()
{
vector<thread> workers;
workers.push_back(thread(abc)); // compiles
thread t(abc);
workers.push_back(t); // does not compile
return 0;
}
UPDATE: i am using g++ 4.4.6 on linux. Below is the error
[jim#cola c++]$ g++ -std=c++0x -pthread -g -Wall t.cpp -o t
In file included from /usr/lib/gcc/x86_64-redhat-linux/4.4.6/../../../../include/c++/4.4.6/x86_64-redhat-linux/bits/c++allocator.h:34,
from /usr/lib/gcc/x86_64-redhat-linux/4.4.6/../../../../include/c++/4.4.6/bits/allocator.h:48,
from /usr/lib/gcc/x86_64-redhat-linux/4.4.6/../../../../include/c++/4.4.6/string:43,
from /usr/lib/gcc/x86_64-redhat-linux/4.4.6/../../../../include/c++/4.4.6/bits/locale_classes.h:42,
from /usr/lib/gcc/x86_64-redhat-linux/4.4.6/../../../../include/c++/4.4.6/bits/ios_base.h:43,
from /usr/lib/gcc/x86_64-redhat-linux/4.4.6/../../../../include/c++/4.4.6/ios:43,
from /usr/lib/gcc/x86_64-redhat-linux/4.4.6/../../../../include/c++/4.4.6/ostream:40,
from /usr/lib/gcc/x86_64-redhat-linux/4.4.6/../../../../include/c++/4.4.6/iostream:40,
from t.cpp:1:
/usr/lib/gcc/x86_64-redhat-linux/4.4.6/../../../../include/c++/4.4.6/thread: In member function ‘void __gnu_cxx::new_allocator<_Tp>::construct(_Tp*, const _Tp&) [with _Tp = std::thread]’:
/usr/lib/gcc/x86_64-redhat-linux/4.4.6/../../../../include/c++/4.4.6/bits/stl_vector.h:737: instantiated from ‘void std::vector<_Tp, _Alloc>::push_back(const _Tp&) [with _Tp = std::thread, _Alloc = std::allocator<std::thread>]’
t.cpp:29: instantiated from here
/usr/lib/gcc/x86_64-redhat-linux/4.4.6/../../../../include/c++/4.4.6/thread:122: error: deleted function ‘std::thread::thread(const std::thread&)’
/usr/lib/gcc/x86_64-redhat-linux/4.4.6/../../../../include/c++/4.4.6/ext/new_allocator.h:105: error: used here
In file included from /usr/lib/gcc/x86_64-redhat-linux/4.4.6/../../../../include/c++/4.4.6/vector:69,
from t.cpp:4:
/usr/lib/gcc/x86_64-redhat-linux/4.4.6/../../../../include/c++/4.4.6/thread: In member function ‘void std::vector<_Tp, _Alloc>::_M_insert_aux(__gnu_cxx::__normal_iterator<typename std::_Vector_base<_Tp, _Alloc>::_Tp_alloc_type::pointer, std::vector<_Tp, _Alloc> >, _Args&& ...) [with _Args = const std::thread&, _Tp = std::thread, _Alloc = std::allocator<std::thread>]’:
/usr/lib/gcc/x86_64-redhat-linux/4.4.6/../../../../include/c++/4.4.6/bits/stl_vector.h:741: instantiated from ‘void std::vector<_Tp, _Alloc>::push_back(const _Tp&) [with _Tp = std::thread, _Alloc = std::allocator<std::thread>]’
t.cpp:29: instantiated from here
/usr/lib/gcc/x86_64-redhat-linux/4.4.6/../../../../include/c++/4.4.6/thread:122: error: deleted function ‘std::thread::thread(const std::thread&)’
/usr/lib/gcc/x86_64-redhat-linux/4.4.6/../../../../include/c++/4.4.6/bits/vector.tcc:314: error: used here
You're getting the error because std::thread is noncopyable, and you're trying to insert a copy of t into the vector.
The only way you could make this work would be to do:
workers.push_back(std::move(t));
However, this would mean that after you do that, t no longer represents a thread (the thread it represented was moved into the vector).
The reason is that std::thread has a move constructor, but doesn't have a copy constructor.
Here is a cleaner and faster solution that requires neither copying nor moving:
workers.emplace_back(abc);
Because std::thread is not copyable, you could move it to vector though:
thread t(abc);
workers.push_back(std::move(t));
Better solution is to store smart pointer in vector:
std::vector<std::shared_ptr<std::thread>> workers;
Because when work with lambda, there is no way to capture a move only type, a workaround is to store a move-only type in std::shared_ptr<std::thread>.