Why do I get Wsign-conversion warning? - c++

I have following code:
template <typename T>
struct wrapper {
T t;
operator T() { return t; }
T get() { return t; }
};
int main() {
int a[10];
int* x = a;
wrapper<long unsigned int> y{2};
std::cout << (x + y); // warning
}
When I compile it on gcc (tested on 7.3.0 and 8.2.0) with -Wsign-conversion I get "warning: conversion to 'long int' from 'long unsigned int' may change the sign of the result". If y has type long unsigned int, there is no warning. Moreover when I explicitly call y.get() there is also no warning:
std::cout << (x + y.get()); // this is ok
Why this is the case? Are there some special rules for pointer arithmetic which cannot be used when using user-defined conversion?

Seems like a compiler issue/bug
(thanks #liliscent for correcting what I said here earlier)
First, let's make a single minimal reproducible example for all of the statement you were mentioning:
#include <iostream>
template <typename T>
struct wrapper {
T t;
operator T() const { return t; }
T get() const { return t; }
};
int main() {
int a[10];
int* x { a } ;
wrapper<long int> y1{2};
wrapper<unsigned int> y2{2};
wrapper<long unsigned int> y3{2};
std::cout << (x + y1) << '\n';
std::cout << (x + y2) << '\n';
std::cout << (x + y3) << '\n'; // this triggers a warning
std::cout << (x + y3.get()) << '\n';
}
and using GCC 8.2.0, we get:
<source>: In function 'int main()':
<source>:20:23: warning: conversion to 'long int' from 'long unsigned int' may change the sign of the result [-Wsign-conversion]
std::cout << (x + y3) << '\n';
^~
Compiler returned: 0
at the link, you will see how:
GCC emits this error with all (recent) versions.
Clang emits this error with version 6.0.
Clang does not emits this error with version 7.0.
So it must be some corner case w.r.t. standards compliance.
... but don't go into "Here Be Dragons" territory.
Now, I'm sure there's a complex technical explanation regarding why you only get an error on the 3rd of these streaming statements. But I claim it does not matter from a practical use perspective. If you stick to only adding proper integers to pointers - like you do with the .get() statement - you wouldn't get that warning.
You see, you are trying to add up a user-defined type to a pointer - which doesn't make much sense generally. It's true that your structure can be converted into an integer, but relying on this conversion to be made implicitly opens you up to things like the choice to convert the other operand, or other conversion paths you have not considered. What's more, these are cases in which you might 'hit' some esoteric clause in the standard regarding when implicit conversions are legit, which may or may not be implemented with perfect correctness by the compiler (see #cppcleaner's comment).
So just use x + y3.get() in your code and you'll not have to worry about these esoteric corner cases.
I have made a similar argument in this answer regarding the use of index 0 of an empty string (yes, that's a thing).

Related

Why can I pass a double for a float argument in a function [duplicate]

This question already has answers here:
Why Implicit Cast from double to float available?
(2 answers)
Closed 1 year ago.
The following code compiles and executed without any issues.
I would have the compiler (GCC 9.2.0) expected to prevent me from this, but it doesn't.
Can anyone point out the background of this effect to me?
#include <iostream>
const double c = 1.23456789;
void print(float f)
{
std::cout << (f == c) << std::endl;
}
int main()
{
double d=1.23456789;
print(d);
std::cout << (d == c) << std::endl;
return 0;
}
output (making clear that some implicit cast took place)
0
1
Why can I pass a double for a flaot argument in a function
Because you're allowed to pass an argument as a parameter of different type into a function if there is an implicit conversion sequence from the argument type to the parameter type. Conversion from double to float is such implicit conversion.
I would have the compiler (GCC 9.2.0) expected to prevent me from this
The compiler wouldn't conform to the language if it prevented from compiling well-formed programs. You can ask the compiler to warn you in this case. The option to enable such warning in your compiler is -Wfloat-conversion

casting int * const to long int * const warning

This may well have been asked in some other way before (I would be surprised if its not) but I am struggling to find it if it is.
Given:
#include <iostream>
#include <string>
int main()
{
int * const pi = new int(1);
long int * const pl = reinterpret_cast<long int * const>(pi);
std::cout << "val: " << *pl << std::endl;
return 0;
}
I get the warning:
<source>: In function 'int main()':
<source>:7:27: warning: type qualifiers ignored on cast result type [-Wignored-qualifiers]
7 | long int * const pl = reinterpret_cast<long int * const>(pi);
| ^~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
ASM generation compiler returned: 0
<source>: In function 'int main()':
<source>:7:27: warning: type qualifiers ignored on cast result type [-Wignored-qualifiers]
7 | long int * const pl = reinterpret_cast<long int * const>(pi);
| ^~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
Execution build compiler returned: 0
Program returned: 0
val: 1
But I am not sure why I get this warning since the re-cast should also be a const pointer - just to a different type. I would like to achieve the same code (which works if ignore the warning), but without the warning.
long int * const is an immutable pointer to mutable data.
reinterpret_cast returns a temporary object. On most temporary objects (including this one), the immutability of the top-level type is irrelevant.
this:
long int * const pl = reinterpret_cast<long int * const>(pi);
is the same as
long int * const pl = reinterpret_cast<long int *>(pi);
the compiler is warning you about it, because presumably you thought typing const there did something, and you are wrong, it did nothing.
Now, this is not directly related to what you asked, but I would be remiss in not mentioning that:
std::cout << "val: " << *pl << std::endl;
results in your program exhibiting undefined behavior. The pointed to object is not a long int, it is an int, and you are accessing it as an object whose type it is not.
reinterpret_cast is not "treat these bytes as a different type", despite many people treating it like it is. Such operations are almost always undefined behavior under the C++ standard.
The proper way to interpret the bytes of object A as an object of type B (when they are suitably trivial types) is to use something like memcpy.
int * const pi = new int(1);
long int l = 0;
static_assert( sizeof(*pi) == sizeof(l) );
::memcpy( &l, pi, sizeof(l) ); // or l = std::bit_cast<long int>(*pi) in C++20
std::cout << "val: " << l << std::endl;
this is legal, if implementation defined output, as you are free to copy bytes of sufficiently trivial types around, and long int is required to have no trap values.
There is no legal way in C++ to have a chunk of memory be read/written to both as a long int and as an int. The amount of aliasing (treating one type as another) in C++ you are allowed to do is limited, because violating aliasing makes certain really powerful optimizations impossible or impractical.
Many programs ignore this fact and rely on the compiler producing "naive" assembly when you do these kind of operations. They are generating code that exhibits undefined behavior.

How to enable warnings on conversion from int to int64_t in gcc or clang

Is it possible to enable warnings in g++ or clang on cast from int to int64_t?
Example:
int n;
cin >> n;
int64_t power = (1 << n);
I want that compiler tells me about this conversion in third line.
You could build something on these lines:
struct my_int64
{
template<class Y> my_int64(const Y&)
{
static_assert(false, "can't do this");
}
template<> my_int64(const long long&) = default;
/*ToDo - you need to hold the data member here, and
supply necessary conversion operators*/
};
Then
int n = 3;
my_int64 power = (1LL << n);
compiles, but
my_int64 power = (1 << n);
will not. In that sense, this is a good starting point. You could hack the preprocessor to use this in place of int64_t.
If you wanted a warning rather than an error, you could replace the static_assert with
my_int64 x{}; Y y = x; and hope the compiler emits a warning for a narrowing conversion, and trust it to optimise out the two statements as they are collectively a no-op.

Ambiguous Syntax

#include <iostream>
using namespace std;
typedef int MYINT;
int main()
{
int y = MYINT(); // As expected, y = 0; value initialization
cout << MYINT(); // Error
cout << sizeof(MYINT()); // Error
}
Why the last two lines in the main function before the closing brace give error? Why is the expression MYINT() treated differently in different contexts? Any Standard reference will be helpful.
MYINT() can, depending on context, be interpreted as an expression of type MYINT, or a specifier of a function type taking no arguments and returning MYINT.
In some situations, where either an expression or a type specifier is valid, this gives an ambiguity; this is resolved by interpreting it as a type specifier if possible (EDIT: C++03 8.2/2, if you want a Standard reference).
sizeof can take either an expression, or a parenthesised type specifier, as it's argument, giving this ambiguity. So here MYINT() is interpreted as a type specifier; then you get an error, since sizeof can't be applied to a function type.
EDIT: You can fix the error by removing the parentheses so it will be interpreted as an expression (sizeof MYINT()), adding extra parentheses so it isn't a valid type specifier (sizeof((MYINT()))), or changing it to the correct type (sizeof(MYINT)).
cout << MYINT() is unambiguous, so there should be no error, and indeed there isn't on my compiler. What is the error, and what is your compiler?
If your MINTINT is typedef int MYINT then MYINT() is not a function but is int() which is a default initialization, equivallent to int y = 0 or int y = int(0).
Your second line, ie cout << MYINT() compiles correctly for me with g++ -Wall -ansi -pedantic for the same reason.
But g++ will complain for the sizeof with the following error error: invalid application of "sizeof" to a function type because it interprets MYINT() as "a call to a default constructor for int" (EDIT: this is not correct) "a function type returning MYINT which is not allowed" (EDIT: this is the correct answer, see Mike's). But this a nothing to do with the typedef.
Summary:
#include <iostream>
typedef int myint;
int main()
{
int y = myint();
int z = myint(0);
std::cout << y << z; // Will output 0 0
std::cout << std::endl << myint(0) << myint(); // Will output 0 0
std::cout << sizeof(int()); // The error is here; same with sizeof(myint())
}
Edit (again)
As said in the comment is the cout lines doesn't work for you, this is because you probably forgot to include <iostream>.
Edit
Look also the answer of Mike Seymour for an explanation of the ambiguity with sizeof.
// OK. Implicit conversion to int.
int y = MYINT();
// OK. Implicit conversion again. Which compiler do you use?
cout << MYINT();
// Invalid. Tries to get size of a function that returns MYINT,
// because sizeof expects a type-id and according to 8.2/2,
// which is forbidden according to the C++ Standard 5.3.3/1
cout << sizeof(MYINT());
// Do you want this instead?
cout << sizeof(MYINT);
I do not see any error for the cout << MYINT(); line. However I see invalid application of 'sizeof' to a function type for the cout << sizeof(MYINT()); line. The problem is the () around MYINT(). The C++ standard says this about sizeof and how it is parsed:
sizeof unary-expression
sizeof ( type-id )
There is a parsing ambiguity between sizeof unary-expression and sizeof ( type-id ). It is resolved by using longer match. It parses sizeof (MYINT()) as sizeof ( type-id ), MYINT() is a function type and thus you see the error.
Why the last two lines in the main function before the closing brace give error?
cout << MYINT(); doesn't work because cout is not defined. Once you do #include <iostream> and using std::cout, it will work fine.
sizeof(MYINT()) does indeed not work, but sizeof(int()) doesn't work either, so that's to be expected. sizeof(MYINT) will work just fine.
Why is the expression MYINT() treated differently in different contexts?
It's not. In every case MYINT() behaves exactly like int().

C++ Switch won't compile with externally defined variable used as case

I'm writing C++ using the MinGW GNU compiler and the problem occurs when I try to use an externally defined integer variable as a case in a switch statement. I get the following compiler error: "case label does not reduce to an integer constant".
Because I've defined the integer variable as extern I believe that it should compile, does anyone know what the problem may be?
Below is an example:
test.cpp
#include <iostream>
#include "x_def.h"
int main()
{
std::cout << "Main Entered" << std::endl;
switch(0)
{
case test_int:
std::cout << "Case X" << std::endl;
break;
default:
std::cout << "Case Default" << std::endl;
break;
}
return 0;
}
x_def.h
extern const int test_int;
x_def.cpp
const int test_int = 0;
This code will compile correctly on Visual C++ 2008. Furthermore a Montanan friend of mine checked the ISO C++ standard and it appears that any const-integer expression should work. Is this possibly a compiler bug or have I missed something obvious?
Here's my compiler version information:
Reading specs from C:/MinGW/bin/../lib/gcc/mingw32/3.4.5/specs
Configured with: ../gcc-3.4.5-20060117-3/configure --with-gcc --with-gnu-ld --with-gnu-as --host=mingw32 --target=mingw32 --prefix=/mingw --enable-threads --disable-nls --enable-languages=c,c++,f77,ada,objc,java --disable-win32-registry --disable-shared --enable-sjlj-exceptions --enable-libgcj --disable-java-awt --without-x --enable-java-gc=boehm --disable-libgcj-debug --enable-interpreter --enable-hash-synchronization --enable-libstdcxx-debug
Thread model: win32
gcc version 3.4.5 (mingw-vista special r3)
A case label requires an integral constant expression which have strict requirements that enable their value to be determined at compile time at the point of use.
From 5.19 [expr.const], "an integral constant expression can involve only literals (2.13), enumerators, const variables or static data members of integral or enumeration types initialized with constant expressions (8.5),...".
At the point at which you use test_int where a constant expression is required, it is a const variable declared extern and without any initializer and does not meet the requirements for a constant expression, despite the fact that you do actually initialize it with a integral constant expression in another translation unit. (*This is not completely clear from the wording of the standard but is my current interpretation of it.)
The restrictions in the standard disallow usages such as:
void f(int a, int b)
{
const int c = b;
switch (a)
{
case c:
//...
}
}
In your example, when the compiler is compiling test.cpp, it has no way to determine what the initializer might be in x_def.cpp. You might have done:
const int test_int = (int)time();
Clearly, in neither of these examples could the value of the const int be determined at compile time which is the intention for integral constant expressions.
Case labels have to be compile-time constants. That means the compiler must be able to substitute the value at compile-time. Although your values are constant, the compiler can't know their values until at least link-time.
VC++ is right, and g++ is wrong. A case label is required to be an integral constant expression (§6.4.2/2) and a const variable of integer type initialized with a constant expression is a constant expression (§5.19/1).
Edit:mostly for Pavel, and his suggestion of a possible DR. §5.19/2 has been completely rewritten already. C++0x adds a whole new concept of a constexpr that expands what's considered a constant expression considerably. For example, under the current standard, something like:
int x() { return 10; }
const int y = x();
y is not a constant expression. We can all easily see that it's (indirectly) initialized with a literal 10, but the compiler still can't allow it as a constant expression. Under the new standard, it'll be possible designate x() as a constexpr, and y will be a constant expression.
As it's formulated in N2960, §5.19/2 says an expression is a constant expression unless it uses something from the following list. It then gives about a page-long list, but using a const variable that isn't initialized in the current compilation unit doesn't seem to be one of them. [Edit: see below -- reading CWG Issue 721, I've changed my mind.]
As far as VC++ being right and g++ wrong, I meant only in this very specific respect. There's no question that both are "wrong" if you're talking about getting every part of the standard correct. I doubt anybody's even working on implementing export for either one.
export does, however, point out a degree to which C++ seems willing to postpone decisions until link time. Two-phase name lookup means that when an exported template is compiled, there's a lot more than just constant expressions that it doesn't know for sure. It might not even know whether a particular name refers to a function or an object -- but there's no question that the standard does require exactly that. The issue at hand strikes me as a substantially simpler one to deal with.
Edit: I did a bit of searching, and found Core Working Group Issue 721. Jame's question parallels the one at hand quite closely ("However, this does not require, as it presumably should, that the initialization occur in the same translation unit and precede the constant expression..."). The proposed resolution adds the phrase: "...with a preceding initialization...". At least as I read it, that means that the committee agreed that under the current standard, the code must be accepted, but under the new standard it's not allowed.
That wording was agreed upon in July of this year, but doesn't (yet?) appear in N2960, which I believe is the most recent draft of C++0x.
I cannot reproduce this on a trivial example using VC++2008:
test.cpp:
extern const int n;
int main() {
switch (0) {
case n: break;
}
}
test2.cpp:
extern const int n = 123;
compile with:
cl.exe test.cpp test2.cpp
output:
test.cpp(4) : error C2051: case expression not constant
MS compiler is being a bit naughty here. When you compile the the constant initialization and the case statement using the constant in the same compilation unit it works out the constant value at compile time.
Once you attempt to use the extern const outside of the compilation unit where it's initialised (i.e. the cpp file containing initialization or any of the files it includes) the compiler will barf with pretty much the same error. Fred Larson is correct the compiler shouldn't know the constant value until link time and thus it must not be acceptable as a switch constant, it's just MS compiler cheats a little bit.
The solution to your problem would be to use macros, is there any reason why you don't want to #define the constant?
Here's a simpler test:
test_int.cpp:
const int test_int = 10;
main.cpp:
#include <iostream>
using std::cout;
using std::endl;
extern const int test_int;
int main() {
cout << test_int << endl;
return 0;
}
In G++, I get an undefined reference. However, doing the same thing in C works. According to http://gcc.gnu.org/ml/gcc/2005-06/msg00325.html , a const variable implicitly has internal linkage in C++. This doesn't appear to be the case in C.
I'm using a "gcc (SUSE Linux) 4.3.2" and having a similar effect, that still is a bit stranger.
My definitions are:
namespace operations{
const cOpDummy OpDummy();
const cInitOperator InitOperator();
};
const unsigned long ulNumberOfOperations = 2;
const cOperation * arrayOperations[] = {
& (operations::OpDummy),
& (operations::InitOperator)
};
And the extern declarations in an other file are:
extern const unsigned long ulNumberOfOperations;
extern const cOperation * arrayOperations[];
The funny thing is: The compiler gives just for "ulNumberOfOperations" "undefined reference to ulNumberOfOperations", but is Ok with "arrayOperations[]".
My workaround is to declare "ulNumberOfOperations" not constant.
Since c++11 you could build a little template framework to give you a syntax like this:
void test(int a, int x, int y, int z)
{
std::cout << "given " << a << ", choosing ";
given(a)
.when(x, [] { std::cout << "x\n"; })
.when(y, [] { std::cout << "y\n"; })
.when(z, [] { std::cout << "z\n"; })
.when(any_other, [] { std::cout << "none of the above\n"; });
}
Full demo:
#include <iostream>
struct any_object {};
constexpr auto any_other = any_object {};
template<class Expr>
struct when_object
{
template<class T, class F>
constexpr when_object& when(T const& value, F&& f)
{
if (not executed and expr == value) {
executed = true;
f();
}
return *this;
}
template<class F>
constexpr void when(any_object, F&& f)
{
if (not executed) {
executed = true;
f();
}
}
Expr const& expr;
bool executed = false;
};
template<class Expr>
constexpr auto given(Expr const& expr)
{
return when_object<Expr> {expr};
}
void test(int a, int x, int y, int z)
{
std::cout << "given " << a << ", choosing ";
given(a)
.when(x, [] { std::cout << "x\n"; })
.when(y, [] { std::cout << "y\n"; })
.when(z, [] { std::cout << "z\n"; })
.when(any_other, [] { std::cout << "none of the above\n"; });
}
int main()
{
test(4, 4, 5, 6);
test(4, 3, 4, 5);
test(4, 2, 3, 4);
test(1, 2, 3, 4);
}
expected results:
given 4, choosing x
given 4, choosing y
given 4, choosing z
given 1, choosing none of the above