The following program compiles fine.
#include <bitset>
#include <cmath>
int main()
{
const int r = std::sqrt(100);
std::bitset<r> n;
}
$ g++ -Wall -Wextra -pedantic -std=c++11 foo.cpp
$
But the following program fails to compile.
#include <bitset>
#include <cmath>
int main()
{
std::bitset<std::sqrt(100)> n;
}
$ g++ -Wall -Wextra -pedantic -std=c++11 bar.cpp
bar.cpp: In function ‘int main()’:
bar.cpp:6:31: error: conversion from ‘__gnu_cxx::__enable_if<true, double>::__type {aka double}’ to ‘long unsigned int’ not considered for non-type template argument
std::bitset<std::sqrt(100)> n;
^
bar.cpp:6:31: error: could not convert template argument ‘std::sqrt<int>(100)’ to ‘long unsigned int’
bar.cpp:6:34: error: invalid type in declaration before ‘;’ token
std::bitset<std::sqrt(100)> n;
^
bar.cpp:6:33: warning: unused variable ‘n’ [-Wunused-variable]
std::bitset<std::sqrt(100)> n;
^
According to me, both C++ programs are equivalent. Why is it that then the second one does not compile whereas the first one does?
Update
Some of the answers are saying that std::sqrt() is generally not declared as constexpr but on gcc has extended it by declaring it constexpr. But it still does not answer my question.
If std::sqrt() is not declared as constexpr, then both programs should fail to compile.
If std::sqrt() is declared as constexpr in gcc, then both programs should compile successfully.
Why is it that only the first program compiles but the second one fails?
The first program might compile for you, but it is not portable because the std::sqrt function is not specified by the standard to be constexpr. It appears that GCC has decided to make it constexpr:
template<typename _Tp>
inline _GLIBCXX_CONSTEXPR
typename __gnu_cxx::__enable_if<__is_integer<_Tp>::__value,
double>::__type
sqrt(_Tp __x)
{ return __builtin_sqrt(__x); }
But another standard library implementation might not have a constexpr std::sqrt function so your first program would not compile there.
Let us instead simplify the code, and modify it a bit so that the relevant concepts are involved exactly:
constexpr std::size_t r = 10.0;
std::bitset<r> b1; // OK
std::bitset<10.0> b2; // ill-formed
It really looks as though the declarations of b1 and b2 should be treated the same way, but the rules for implicit conversions of template arguments are more strict than the rules for implicit conversions elsewhere.
According to the standard, when the type of a template argument (here, double) differs from the type of the template parameter it is being passed to (here, std::size_t), only a restricted set of conversion is allowed, namely "conversions permitted in a converted constant expression" ([temp.arg.nontype]/5). According to [expr.const]/3, a converted constant expression can involve only the following conversions:
user-defined conversions
lvalue-to-rvalue conversions
integral promotions
integral conversions other than narrowing conversions
A floating-integral conversion is not allowed in this context, even though it is allowed in the initialization of r.
Related
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 ?
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.
I decided to test compile a project with -Wsign-conversion enabled, to see what warnings would come up, and came across something that doesn't seem right, where gcc behaves differently than clang. Can someone please tell me which is correct?
I have a function that takes a size_t param:
void func(size_t) {}
some other struct
struct Test {};
and calling code
int i = some_initialiser();
func(sizeof(Test) + static_cast<size_t>(i));
So from my understanding, sizeof returns size_t, and arithmetic between two variables of type size_t should return a size_t, so there shouldn't be any conversion here other than my static_cast, but gcc gives me the warning
warning: conversion to ‘long unsigned int’ from ‘int’ may change the sign of the result [-Wsign-conversion]
Clang doesn't warn here, but does warn if I remove the static_cast in the function call, as expected.
This is a known bug in gcc, fixed in versions 9.3.0 and above.
The warning is valid (compilers can warn about anything they like), but gcc's behavior contradicts its own documentation. There is an existing bug report for this problem (see below).
Here's a simpler test case that illustrates the issue:
#include <cstddef>
int main() {
int i = 42;
size_t s0 = sizeof (int) + (size_t)i;
size_t s1 = sizeof (int) + static_cast<size_t>(i);
}
When I compile it on my system using gcc 9.1.0, I get:
$ g++ -Wsign-conversion -c c.cpp
c.cpp: In function ‘int main()’:
c.cpp:4:32: warning: conversion to ‘long unsigned int’ from ‘int’ may change the sign of the result [-Wsign-conversion]
4 | size_t s0 = sizeof (int) + (size_t)i;
| ^~~~~~~~~
c.cpp:5:32: warning: conversion to ‘long unsigned int’ from ‘int’ may change the sign of the result [-Wsign-conversion]
5 | size_t s1 = sizeof (int) + static_cast<size_t>(i);
| ^~~~~~~~~~~~~~~~~~~~~~
$
Note that the warning occurs both for a C-style cast and for a static_cast.
It's true that the conversion may change the sign of the result (converting a negative int to size_t yields a positive result), but gcc's documentation for -Wsign-conversion says:
'-Wsign-conversion'
Warn for implicit conversions that may change the sign of an
integer value, like assigning a signed integer expression to an
unsigned integer variable. An explicit cast silences the warning.
In C, this option is enabled also by '-Wconversion'.
In this case, an explicit cast is not silencing the warning.
This bug had already been reported:
Bug 87519 - -Wsign-conversion -Wconversion explicit cast fails to silence warning
The fix is commit 61e52125c935279af11b10d27060a96bff7477a4 in the gcc git repo, committed 2019-08-08.
The warning is correct.
If i has a negative value the casting will be problematic. Your function should return an unsigned value (e.g. unsigned int).
From GCC documentation - https://gcc.gnu.org/onlinedocs/gcc/Warning-Options.html:
For C++, also warn for confusing overload resolution for user-defined conversions; and conversions that never use a type conversion operator: conversions to void, the same type, a base class or a reference to them. Warnings about conversions between signed and unsigned integers are disabled by default in C++ unless -Wsign-conversion is explicitly enabled.
Consider following program:
#include <iostream>
template <typename T>
void foo(const T* x) {
x();
}
void bar() { std::cout<<"bar() is called\n"; }
int main() {
foo(bar);
}
It compiles fine on clang++ & VC++ but g++ gives following compiler error (See live demo here )
main.cpp: In function 'int main()':
main.cpp:10:9: error: no matching function for call to 'foo(void (&)())'
foo(bar);
^
main.cpp:3:6: note: candidate: template<class T> void foo(const T*)
void foo(const T* x) {
^~~
main.cpp:3:6: note: template argument deduction/substitution failed:
main.cpp:10:9: note: types 'const T' and 'void()' have incompatible cv-qualifiers
foo(bar);
^
I've used -pedantic-errors when using g++ & clang++ and I've used /W4 & /Zaoption when using VC++ compiler. See live demo here & here. So, I want to know how template type parameter T will be deduced here ? If I remove const from the program then it compiles fine on g++ also. If I use const T& then it compiles fine on all 3 compilers. So, how exactly type will be deduced here in these cases ?
Update:
This program fails in compilation on Intel C++ compiler also. See live demo here . So, is this bug in g++ & Intel C++ or bug in Clang++ & VC++ ?
This is essentially CWG issue 1584:
It is not clear whether the following is well-formed or not:
void foo(){}
template<class T> void deduce(const T*) { }
int main() {
deduce(foo);
}
Implementations vary in their treatment of this example.
Which is currently still active. It's not really possible to say which compiler is right. Though as the note from 2015 indicates, the consensus in the CWG is currently that this should be rejected.
To give a little more context, we must remember that a function type with a cv-qualifier-seq has special meaning (think member functions), and is not simply a type the designates something which may not be modified. Moreover, you can't even add the cv qualification in some sneaky manner, as [dcl.fct]/7 illustrates:
The effect of a cv-qualifier-seq in a function declarator is not the
same as adding cv-qualification on top of the function type. In the
latter case, the cv-qualifiers are ignored. [ Note: A function type
that has a cv-qualifier-seq is not a cv-qualified type; there are no
cv-qualified function types. — end note ][ Example:
typedef void F();
struct S {
const F f; // OK: equivalent to: void f();
};
— end example ]
There is no way in the language to form a const qualified function type. And yet, the deduction we need is to have const T deduced as void(). The former is a const qualified type, and it must also be a function type. But that's a type that cannot exist! So how can it be deduced?!
On the other hand there is machinery in the standard to deduce it if you were using a reference instead of a pointer.
So it isn't that clear how this should be resolved. On the one hand the wording today doesn't allow it per-se, but on the other hand machinery for it is already in place for references. So some implementations go ahead and do the same for pointers.
Why does the following code not compile under g++ (C++14), MSVC (C++14), or ARM (C++03)?
The named Error instance calls the integer constructor, but the anonymous Error instance does not resolve.
class Error
{
public:
Error(int err) : code_(err) {}
const int code_;
};
enum Value
{
value_1
};
int main()
{
// compiles
Error e(value_1);
// does not compile under G++, ARM, or MSVC
Error(value_1);
}
Example error under G++: (Coliru link)
g++ -std=c++14 -O2 -Wall -pedantic -pthread main.cpp && ./a.out
main.cpp: In function 'int main()':
main.cpp:19:18: error: no matching function for call to 'Error::Error()'
Error(value_1);
^
main.cpp:4:5: note: candidate: Error::Error(int)
Error(int err) : code_(err) {}
^~~~~
main.cpp:4:5: note: candidate expects 1 argument, 0 provided
main.cpp:1:7: note: candidate: constexpr Error::Error(const Error&)
class Error
^~~~~
main.cpp:1:7: note: candidate expects 1 argument, 0 provided
main.cpp:1:7: note: candidate: constexpr Error::Error(Error&&)
main.cpp:1:7: note: candidate expects 1 argument, 0 provided
This comes from the same place as "The Most Vexing Parse" - the rule that if it can be a declaration, it is a declaration.
And surprisingly, you're allowed to put parentheses around the identifier in a variable declaration.
(I have no idea why, but I'm guessing that it simplified C's parser back in the day.)
The following are all valid declarations of int variables:
int (foo);
int (bar) = 0;
int (baz)(3);
int (twaddle)(baz);
The problem is that code
Error(value_1);
is a declaration of a variable value_1 of type Error.
This is a legacy from C language that uses expressions as part of type declaration.
For example int *i is a pointer to int because it says that expression *i should evaluate to type int. More examples of this:
int (*func)() is a pointer to function returning int because expression (*func)() evaluates to type int.
int *p[8] is an array of pointers to int because expression *p[x] evaluates to type int.
int (*p)[8] is a pointer to array of 8 int's (int[8]) because expression (*p)[x] evaluates to type int.
int (*(*p[8])())() is an array of 8 pointers to functions returning pointers to a function returning int because expression (*(*p[x])())() evaluates to type int.
Similarly int (i) is a plain variable of type int as expression (i) evaluates to type int.
And so because C++ inherits this from C, it uses parenthesis as part of type declaration, but also adds more syntax on top, leading to some unexpected results.
The rule applied by C++ here says to treat everything that can be a declaration as a declaration.
Similar confusion if often caused by code like this:
Error ec();
which is a forward declaration of a function ec that returns Error.
main.cpp:19:18: error: no matching function for call to 'Error::Error()'
Error(value_1);
The compiler tries to call the non-existent default constructor Error::Error() because it sees
Error(value_1);
as a variable declaration
Error value_1;
A declaration is allowed to have redundant parenthesis.
GCC 7.2 and clang 5.0 seem to disagree about the following code when compiled with -std=c++17 -Werror -Wconversion:
#include <iostream>
#include <string>
#include <string_view>
int main(int argc, char* argv[]) {
std::string s = "Hello, World";
std::string_view vs{s};
std::cout << std::string{vs} << '\n';
return EXIT_SUCCESS;
}
Compiled with clang++ -std=c++17 -Wall -Werror -Wconversion, it compiles correctly (see https://godbolt.org/g/JZn8cL)
However, GCC 7.2 issues a somewhat perplexing diagnostic (see https://godbolt.org/g/kmYbJ3):
<source>: In function 'int main(int, char**)':
7 : <source>:7:26: error: choosing 'std::__cxx11::basic_string<_CharT, _Traits, _Alloc>::operator std::__cxx11::basic_string<_CharT, _Traits, _Alloc>::__sv_type() const [with _CharT = char; _Traits = std::char_traits<char>; _Alloc = std::allocator<char>; std::__cxx11::basic_string<_CharT, _Traits, _Alloc>::__sv_type = std::basic_string_view<char>]' over 'constexpr std::basic_string_view<_CharT, _Traits>::basic_string_view(const std::basic_string_view<_CharT, _Traits>&) [with _CharT = char; _Traits = std::char_traits<char>]' [-Werror=conversion]
std::string_view vs{s};
^
7 : <source>:7:26: error: for conversion from 'std::__cxx11::string {aka std::__cxx11::basic_string<char>}' to 'std::basic_string_view<char>' [-Werror=conversion]
7 : <source>:7:26: note: because conversion sequence for the argument is better
cc1plus: all warnings being treated as errors
Compiler exited with result code 1
Both compilers compile the code fine after changing vs{s} to vs(s).
Which compiler is right here? Is brace initialization of a std::string_view from a std::string running afoul of conversion rules somehow, and clang is failing to issue a diagnostic when it could/should? Or is GCC in the wrong and the diagnostic is erroneous?
Which compiler is right here?
Both? Compilers are allowed to give warnings, or not, as they choose. It's just that when you write:
std::string_view vs{s};
You might be under the impression that this is doing something like aggregate initialization or at least invoking a string_view constructor, but it's not - and maybe that impression isn't as likely to happen if you wrote:
std::string_view vs(s);
so gcc gives you a warning (since you asked for one).
The code is fine. Both compilers are fine. Just use ()s to initialize, there's no reason to use {} here (there is no difference in behavior in the two approaches).
Your original question ("is this invalid C++?") has now been answered ("no."), but it's clear from the comments you're now interested in why gcc warns about this valid code. That is a more interesting question.
Removing the template arguments, the warning reduces to this:
warning: choosing 'std::string::operator std::string_view()' over 'constexpr std::string_view(const std::string_view&) ' [-Wconversion]
So it's warning about a user-defined conversion operator being chosen over a constructor... but that constructor couldn't actually be used for conversion. Here is another example that warns for the same reason:
class A { // Like string_view
public:
A() {}
};
class B { // Like string
public:
operator A() {return A();}
};
int main() {
B b;
A a{b};
}
This gives:
<source>: In function 'int main()':
12 : <source>:12:10: warning: choosing 'B::operator A()' over 'constexpr A::A(A&&)' [-Wconversion]
A a{b};
^
12 : <source>:12:10: warning: for conversion from 'B' to 'A' [-Wconversion]
12 : <source>:12:10: note: because conversion sequence for the argument is better
12 : <source>:12:10: warning: choosing 'B::operator A()' over 'constexpr A::A(const A&)' [-Wconversion]
12 : <source>:12:10: warning: for conversion from 'B' to 'A' [-Wconversion]
12 : <source>:12:10: note: because conversion sequence for the argument is better
By the way, adding e.g. A(int) constructor does not add anything to the warning, so it is focused on the move constructor and copy constructor of A. Deleting the copy and move constructors with =delete does not change the warning either.
This clarifies what is causing the warning, but not why it warns. The gcc documentation for -Wconversion says:
Warn for implicit conversions that may alter a value. This includes conversions between real and integer, like abs (x) when x is double; conversions between signed and unsigned, like unsigned ui = -1; and conversions to smaller types, like sqrtf (M_PI). Do not warn for explicit casts like abs ((int) x) and ui = (unsigned) -1, or if the value is not changed by the conversion like in abs (2.0). Warnings about conversions between signed and unsigned integers can be disabled by using -Wno-sign-conversion.
For C++, also warn for confusing overload resolution for user-defined conversions; and conversions that never use a type conversion operator: conversions to void, the same type, a base class or a reference to them. Warnings about conversions between signed and unsigned integers are disabled by default in C++ unless -Wsign-conversion is explicitly enabled.
So it depends on what you consider to be "confusing overload resolution for user-defined conversions". Perhaps the gcc devs consider A a{b} to look like a call to a constructor of A, and so they consider it calling B::operator A() to be "confusing".
Or perhaps it is indeed a bug... Given that this seems to be a rarely-used warning flag (it's not even in -Wall), and given how strange the "choosing x over y" messages are, that is quite possible.