Why does -Werror=unused-variable not detect some unused variables? [duplicate] - c++

This question already has answers here:
A variable not detected as not used
(3 answers)
Closed 6 years ago.
#include <vector>
class Object
{
};
int main()
{
Object myObject;
std::vector<int> myVector;
}
Compiler emits:
warning: unused variable 'myObject' [-Wunused-variable]
No warning for myVector. Why? Is there any way to enable this?

Whether declaring (and thus initialising and at some point destructung) an arbitrary object has visible side effects cannot be determined in general. The constructor may be calling functions whose definition is not known to the compiler or it may depend on external state or any other aspect which makes the problem undecidable.
In your first case, the constructor is trivial (not even declared), same for the destructor. As Object does not have members, it is clear and easily detectable that Object foo does in fact nothing.
std::vector has a non-trivial constructor which may be allocating memory (external state + function whose definition may not be known (new ...)) together with a non-trivial destructor (also external state + function whose definition may not be known (delete ...)). Reasoning about whether it is safe to remove the declaration (thus emitting a warning hinting that you maybe should) is not possible in this case, thus the compiler has to leave the declaration in the code (and must assume that the declaration is there for a reason).
A prime example is std::lock_guard which is used to lock a mutex when it is constructed and unlock it automatically when it is destructed. The mutex is thus held as long as the object is in scope; generally you would not access the std::lock_guard object at all though—nevertheless it is useful to have it declared. This is the RAII principle at work.
Emitting a warning in such cases would be a nuisance, leading to people turning off the warning, which in turn would render the warning useless. (The compiler may even be designed in such a way that it only emits the warning if it has removed the declaration during optimisation, which is also the reason why some warnings only show up if certain optimisations are enabled.)

This warning is generated only for trivial types. Compiler is not able to find whether construct will call any external function. If you add a constructor to your Object class then compiler will also issue a warning. Gcc allows to tag types which should generate this warning, you can do this using __attribute__((warn_unused)) :
http://coliru.stacked-crooked.com/a/0130c8ef29c121a1
example:
class __attribute__((warn_unused)) Object
{
public:
Object(){}
void use() {}
};
int main()
{
Object myObject; // will give : warning: unused variable 'myObject' [-Wunused-variable]
//myObject.use(); // uncomment to hide this warning
}
[edit]
from gcc attributes page: https://gcc.gnu.org/onlinedocs/gcc/C_002b_002b-Attributes.html :
warn_unused For C++ types with non-trivial constructors and/or
destructors it is impossible for the compiler to determine whether a
variable of this type is truly unused if it is not referenced. This
type attribute informs the compiler that variables of this type should
be warned about if they appear to be unused, just like variables of
fundamental types. This attribute is appropriate for types which just
represent a value, such as std::string; it is not appropriate for
types which control a resource, such as std::lock_guard.
This attribute is also accepted in C, but it is unnecessary because C
does not have constructors or destructors.

As well as the answers above, also check your compiler documentation. Some compilers can be set so that they do not show the same warning multiple times. If you comment out the declaration of "myObject", you may then get the same warning for "myVector". With "mObject" producing that warning first, you wouldn't get a warning for "myVector".
Compiler behaviour in the case of warnings is very compiler-specific, so don't assume that all compilers work the same. :)

Related

Paradox with default c'tor potentially throwing for a class with a std::string member

Do the C++ "Core Guidelines" prevent (or, at least, strongly discourage) a class with a std::string member from having a default constructor? I ask because of an issue I've noticed when using the MSVC Code Analysis tool (in Visual Studio 2019) in its most 'severe' setting; the issue is the "C26455" warning, as discussed in this document:
The C++ Core Guidelines suggest that default constructors shouldn't do
anything that can throw. If the default constructor is allowed to
throw, operations such as move and swap will also throw which is
undesirable because move and swap should always succeed. Parameterized
constructors may throw.
The problem is illustrated in the following, trivial code sample (when compiled using the C++17 Standard – see the "edit" note, below):
#include <string>
class Person {
private:
std::string my_name;
public:
// C26455: Default constructor may not throw. Declare it 'noexcept'...
Person() : my_name{ "Anonymous" } {}
// // C26447: The function is declared 'noexcept' but calls function 'allocator<char> >()' which may throw exceptions...
// Person() noexcept : my_name{ "Anonymous" } {}
};
Now, if I heed the generated C26455 warning (as shown in the comment), and add the noexcept specifier to the default constructor (that commented-out in the code sample above), I get a different warning, indicating that my constructor calls a routine in the standard library that may throw:
warning C26447: The function is declared 'noexcept' but calls function
'allocator >()' which may throw exceptions (f.6).
Note that I get the same warning if I remove the initializer list from the constructor and add that default value initializer to the declaration of the my_name member (as std::string my_name{ "Anonymous" };). However, all warnings disappear if I don't explicitly initialize the my_name member – which means I rely on the default constructor for std::string to create an empty object of unspecified capacity.
Presumably, another option would be to provide my own, non-throwing allocator, and pass that as the second argument to the my_name constructor – but this seems, to me, to be a somewhat over-complex solution to what must be a very common use case. (Similar situations would arise when using other standard library container objects as class members that require initialization involving allocation.)
Is there a simple resolution to this issue? Or should I just ignore/disable those code analysis warnings and core guidelines?
EDIT: Note that, when compiling to the C++20 Standard, the C26447 warning goes away when adding the noexcept specifier to the default constructor. I don't understand what C++20 offers that makes this happen; if I did, then maybe that would give a hint as to how to handle the situation for code compiled using the C++17 Standard (as I would prefer).
In my earlier (now deleted) answer, I speculated that it was that the string literal operator (""s) has a constexpr version from C++20 onwards, but it appears that this cannot, in itself, provide the explanation.

Does an unused member variable take up memory?

Does initializing a member variable and not referencing/using it further take up RAM during runtime, or does the compiler simply ignore that variable?
struct Foo {
int var1;
int var2;
Foo() { var1 = 5; std::cout << var1; }
};
In the example above, the member 'var1' gets a value which is then displayed in the console. 'Var2', however, is not used at all. Therefore writing it to memory during runtime would be a waste of resources. Does the compiler take these kinds of situations into an account and simply ignore unused variables, or is the Foo object always the same size, regardless of whether its members are used?
The golden C++ "as-if" rule1 states that, if the observable behavior of a program doesn't depend on an unused data-member existence, the compiler is allowed to optimized it away.
Does an unused member variable take up memory?
No (if it is "really" unused).
Now comes two questions in mind:
When would the observable behavior not depend on a member existence?
Does that kind of situations occurs in real life programs?
Let's start with an example.
Example
#include <iostream>
struct Foo1
{ int var1 = 5; Foo1() { std::cout << var1; } };
struct Foo2
{ int var1 = 5; int var2; Foo2() { std::cout << var1; } };
void f1() { (void) Foo1{}; }
void f2() { (void) Foo2{}; }
If we ask gcc to compile this translation unit, it outputs:
f1():
mov esi, 5
mov edi, OFFSET FLAT:_ZSt4cout
jmp std::basic_ostream<char, std::char_traits<char> >::operator<<(int)
f2():
jmp f1()
f2 is the same as f1, and no memory is ever used to hold an actual Foo2::var2. (Clang does something similar).
Discussion
Some may say this is different for two reasons:
this is too trivial an example,
the struct is entirely optimized, it doesn't count.
Well, a good program is a smart and complex assembly of simple things rather than a simple juxtaposition of complex things. In real life, you write tons of simple functions using simple structures than the compiler optimizes away. For instance:
bool insert(std::set<int>& set, int value)
{
return set.insert(value).second;
}
This is a genuine example of a data-member (here, std::pair<std::set<int>::iterator, bool>::first) being unused. Guess what? It is optimized away (simpler example with a dummy set if that assembly makes you cry).
Now would be the perfect time to read the excellent answer of Max Langhof (upvote it for me please). It explains why, in the end, the concept of structure doesn't make sense at the assembly level the compiler outputs.
"But, if I do X, the fact that the unused member is optimized away is a problem!"
There have been a number of comments arguing this answer must be wrong because some operation (like assert(sizeof(Foo2) == 2*sizeof(int))) would break something.
If X is part of the observable behavior of the program2, the compiler is not allowed to optimized things away. There are a lot of operations on an object containing an "unused" data-member which would have an observable effect on the program. If such an operation is performed or if the compiler cannot prove none is performed, that "unused" data-member is part of the observable behavior of the program and cannot be optimized away.
Operations that affect the observable behavior include, but are not limited to:
taking the size of a type of object (sizeof(Foo)),
taking the address of a data member declared after the "unused" one,
copying the object with a function like memcpy,
manipulating the representation of the object (like with memcmp),
qualifying an object as volatile,
etc.
1)
[intro.abstract]/1
The semantic descriptions in this document define a parameterized nondeterministic abstract machine. This document places no requirement on the structure of conforming implementations. In particular, they need not copy or emulate the structure of the abstract machine. Rather, conforming implementations are required to emulate (only) the observable behavior of the abstract machine as explained below.
2) Like an assert passing or failing is.
It's important to realize that the code the compiler produces has no actual knowledge of your data structures (because such a thing doesn't exist on assembly level), and neither does the optimizer. The compiler only produces code for each function, not data structures.
Ok, it also writes constant data sections and such.
Based on that, we can already say that the optimizer won't "remove" or "eliminate" members, because it doesn't output data structures. It outputs code, which may or may not use the members, and among its goals is saving memory or cycles by eliminating pointless uses (i.e. writes/reads) of the members.
The gist of it is that "if the compiler can prove within the scope of a function (including functions that were inlined into it) that the unused member makes no difference for how the function operates (and what it returns) then chances are good that the presence of the member causes no overhead".
As you make the interactions of a function with the outside world more complicated/unclear to the compiler (take/return more complex data structures, e.g. a std::vector<Foo>, hide the definition of a function in a different compilation unit, forbid/disincentivize inlining etc.), it becomes more and more likely that the compiler cannot prove that the unused member has no effect.
There are no hard rules here because it all depends on the optimizations the compiler makes, but as long as you do trivial things (such as shown in YSC's answer) it's very likely that no overhead will be present, whereas doing complicated things (e.g. returning a std::vector<Foo> from a function too large for inlining) will probably incur the overhead.
To illustrate the point, consider this example:
struct Foo {
int var1 = 3;
int var2 = 4;
int var3 = 5;
};
int test()
{
Foo foo;
std::array<char, sizeof(Foo)> arr;
std::memcpy(&arr, &foo, sizeof(Foo));
return arr[0] + arr[4];
}
We do non-trivial things here (take addresses, inspect and add bytes from the byte representation) and yet the optimizer can figure out that the result is always the same on this platform:
test(): # #test()
mov eax, 7
ret
Not only did the members of Foo not occupy any memory, a Foo didn't even come into existence! If there are other usages that can't be optimized then e.g. sizeof(Foo) might matter - but only for that segment of code! If all usages could be optimized like this then the existence of e.g. var3 does not influence the generated code. But even if it is used somewhere else, test() would remain optimized!
In short: Each usage of Foo is optimized independently. Some may use more memory because of an unneeded member, some may not. Consult your compiler manual for more details.
The compiler will only optimise away an unused member variable (especially a public one) if it can prove that removing the variable has no side effects and that no part of the program depends on the size of Foo being the same.
I don't think any current compiler performs such optimisations unless the structure isn't really being used at all. Some compilers may at least warn about unused private variables but not usually for public ones.
In general, you have to assume that you get what you have asked for, for example, the "unused" member variables are there.
Since in your example both members are public, the compiler cannot know if some code (particularly from other translation units = other *.cpp files, which are compiled separately and then linked) would access the "unused" member.
The answer of YSC gives a very simple example, where the class type is only used as a variable of automatic storage duration and where no pointer to that variable is taken. There, the compiler can inline all the code and can then eliminate all the dead code.
If you have interfaces between functions defined in different translation units, typically the compiler does not know anything. The interfaces follow typically some predefined ABI (like that) such that different object files can be linked together without any problems. Typically ABIs do not make a difference if a member is used or not. So, in such cases, the second member has to be physically in the memory (unless eliminated out later by the linker).
And as long as you are within the boundaries of the language, you cannot observe that any elimination happens. If you call sizeof(Foo), you will get 2*sizeof(int). If you create an array of Foos, the distance between the beginnings of two consecutive objects of Foo is always sizeof(Foo) bytes.
Your type is a standard layout type, which means that you can also access on members based on compile-time computed offsets (cf. the offsetof macro). Moreover, you can inspect the byte-by-byte representation of the object by copying onto an array of char using std::memcpy. In all these cases, the second member can be observed to be there.
The examples provided by other answers to this question which elide var2 are based on a single optimization technique: constant propagation, and subsequent elision of the whole structure (not the elision of just var2). This is the simple case, and optimizing compilers do implement it.
For unmanaged C/C++ codes the answer is that the compiler will in general not elide var2. As far as I know there is no support for such a C/C++ struct transformation in debugging information, and if the struct is accessible as a variable in a debugger then var2 cannot be elided. As far as I know no current C/C++ compiler can specialize functions according to elision of var2, so if the struct is passed to or returned from a non-inlined function then var2 cannot be elided.
For managed languages such as C#/Java with a JIT compiler the compiler might be able to safely elide var2 because it can precisely track if it is being used and whether it escapes to unmanaged code. The physical size of the struct in managed languages can be different from its size reported to the programmer.
Year 2019 C/C++ compilers cannot elide var2 from the struct unless the whole struct variable is elided. For interesting cases of elision of var2 from the struct, the answer is: No.
Some future C/C++ compilers will be able to elide var2 from the struct, and the ecosystem built around the compilers will need to adapt to process elision information generated by compilers.
It's dependent on your compiler and its optimization level.
In gcc, if you specify -O, it will turn on the following optimization flags:
-fauto-inc-dec
-fbranch-count-reg
-fcombine-stack-adjustments
-fcompare-elim
-fcprop-registers
-fdce
-fdefer-pop
...
-fdce stands for Dead Code Elimination.
You can use __attribute__((used)) to prevent gcc eliminating an unused variable with static storage:
This attribute, attached to a variable with static storage, means that
the variable must be emitted even if it appears that the variable is
not referenced.
When applied to a static data member of a C++ class template, the
attribute also means that the member is instantiated if the class
itself is instantiated.

Why the compiler does not issue a warning when an object std::vector is declared but never used? [duplicate]

This question already has answers here:
A variable not detected as not used
(3 answers)
Closed 6 years ago.
#include <vector>
class Object
{
};
int main()
{
Object myObject;
std::vector<int> myVector;
}
Compiler emits:
warning: unused variable 'myObject' [-Wunused-variable]
No warning for myVector. Why? Is there any way to enable this?
Whether declaring (and thus initialising and at some point destructung) an arbitrary object has visible side effects cannot be determined in general. The constructor may be calling functions whose definition is not known to the compiler or it may depend on external state or any other aspect which makes the problem undecidable.
In your first case, the constructor is trivial (not even declared), same for the destructor. As Object does not have members, it is clear and easily detectable that Object foo does in fact nothing.
std::vector has a non-trivial constructor which may be allocating memory (external state + function whose definition may not be known (new ...)) together with a non-trivial destructor (also external state + function whose definition may not be known (delete ...)). Reasoning about whether it is safe to remove the declaration (thus emitting a warning hinting that you maybe should) is not possible in this case, thus the compiler has to leave the declaration in the code (and must assume that the declaration is there for a reason).
A prime example is std::lock_guard which is used to lock a mutex when it is constructed and unlock it automatically when it is destructed. The mutex is thus held as long as the object is in scope; generally you would not access the std::lock_guard object at all though—nevertheless it is useful to have it declared. This is the RAII principle at work.
Emitting a warning in such cases would be a nuisance, leading to people turning off the warning, which in turn would render the warning useless. (The compiler may even be designed in such a way that it only emits the warning if it has removed the declaration during optimisation, which is also the reason why some warnings only show up if certain optimisations are enabled.)
This warning is generated only for trivial types. Compiler is not able to find whether construct will call any external function. If you add a constructor to your Object class then compiler will also issue a warning. Gcc allows to tag types which should generate this warning, you can do this using __attribute__((warn_unused)) :
http://coliru.stacked-crooked.com/a/0130c8ef29c121a1
example:
class __attribute__((warn_unused)) Object
{
public:
Object(){}
void use() {}
};
int main()
{
Object myObject; // will give : warning: unused variable 'myObject' [-Wunused-variable]
//myObject.use(); // uncomment to hide this warning
}
[edit]
from gcc attributes page: https://gcc.gnu.org/onlinedocs/gcc/C_002b_002b-Attributes.html :
warn_unused For C++ types with non-trivial constructors and/or
destructors it is impossible for the compiler to determine whether a
variable of this type is truly unused if it is not referenced. This
type attribute informs the compiler that variables of this type should
be warned about if they appear to be unused, just like variables of
fundamental types. This attribute is appropriate for types which just
represent a value, such as std::string; it is not appropriate for
types which control a resource, such as std::lock_guard.
This attribute is also accepted in C, but it is unnecessary because C
does not have constructors or destructors.
As well as the answers above, also check your compiler documentation. Some compilers can be set so that they do not show the same warning multiple times. If you comment out the declaration of "myObject", you may then get the same warning for "myVector". With "mObject" producing that warning first, you wouldn't get a warning for "myVector".
Compiler behaviour in the case of warnings is very compiler-specific, so don't assume that all compilers work the same. :)

Should constructor initialize all the data members of the class?

I have a situation like this:
class A {
public:
A() : n(0) {}
private:
int n;
int m;
}
There is simply no meaning in the application logic to initialize m in the constructor. However, Eclipse warns me that the constructor leaves m uninitialized. I can't run the code somewhere else now. The warning is:
Member 'm' was not initialized in this constructor
So, does C++ encourage us to initialize all the data members in the constructor or it is just Eclipse's logic?
Should constructor initialize all the data members of the class?
That would be a good practice.
So, does C++ encourage us to initialize all the data members in the constructor?
It's not required by the c++ standard. As long as you initialize all variables before they're used, your program is correct in that regard.
or it is just Eclipse's logic?
Quite likely. Neither g++ nor clang versions that I tested warn about this when all warnings are enabled. The logic may or might not be based on high integrity c++ coding standard
12.4.2 or some other coding standard or style guide.
C++ doesn't require attributes to be initialized in constructor, except in case of const attributes where there value must be defined in initialization list.
However, it is clearly a good practice to initialize every attributes in constructor. I cannot count how many bugs I've met due to a non initialized variable or attributes.
Finally, every object should permanently be in a consistent state, which include both public (accessible) attributes and private attributes as well. Optimization should not be a reason for keeping an object un-consistent.
Fully disagree with all the answers and comments. There is absolutely no need to default initialze a member when it is not needed. This is why C/C++ never initializes built-in types as members or automatic variables - because doing so would impede performance. Of course, it is not a problem when you create your object/variable once (that's why statics are default-initialized), but for something happening in a tight loop default initialization might eat valuable nanoseconds.
The one exception to this rule would, in my view, be pointers (if you happen to have raw pointers in your code). Raw pointers should be NULL-initialized, since having invalid pointer is a direct way to undefined behaviour.
For completeness, the warning comes from the C/C++ Code Analysis. In particular the problem is Potential Programming Problems / Class members should be properly initialized
To change the code analysis settings (in this case I recommend per-project) edit the project properties. You can disable the whole warning, or disable it on just the files that violate the warning condition.
As for comparing CDT with GCC or CLang, this appears to be a case where additional code analysis is being done by CDT compared to what is available from the compilers. Of course that is to be expected as the CDT Code Analysis' remit is greater than that of the compiler.
PS, If you are up for it, you can read the implementation of this particular checker.
As it has been already said, you should always initialize pointers and of course const objects are mandatory.
In my opinion you should not initialize when it is not necessary but it is good to check for all non constructor initialized variables once in a while because they are source of very frequent and hard to find bugs.
I run Cppcheck every few months. This gives me more than one hundred 'false' warnings like "Member variable 'foo::bar' is not initialized in the constructor." but once in a while it discovers some real bugs so it is totally worth it.

How to workaround Visual C++ standards non compliance?

According to this question, Visual C++ 2005 (and also 2008/2010) does not zero initialize correctly data members.
Since I have a code which requires the standard behaviour, and which crashes in release mode (and not in debug), I suspect the issue comes from here.
Now, the problem is that the code base is quite large, and manually inspecting classes is difficult.
Is there a compiler option to trigger a warning on this non standard behaviour of MSVC ? With /W4, you get the warnings about some non standard extensions (conversions from rvalues to references, missing typename keyword), but not for this particular problem.
EDIT: I suspect code like that to cause trouble (pasted from the linked question)
include <cassert>
struct B { ~B(); Foo* m; };
int main()
{
B * b= new B();
assert ( b->m ==0);
}
in other portions of the code I have things like
B* b = new B();
and then, later,
if (b->m) { b->m->bar(); }
and b->m should be zero per the standard, but it's likely not (except in debug mode). I would like to detect such code (like a warning "m used without being initialized" or something)
Is this about foo::foo () : member () { }? Paste some example (code) of the problem you are seeing. In standard C++ implementation, the member should have value 0 (assuming it is of a fundamental type). However some older compilers do not do/implement this correctly and just leave it uninitialized. AFAIK there is no warning for this. You will have to go through the code and initialize the member with 0 or other value explicitly.
asper charles comment below, the compiler should be zero init before the ctor is called.
8.5 An object whose initializer is an empty set of parentheses, i.e., (), shall be
value-initialized. ... Value-initialization for such a class object may be implemented
by zero-initializing the object and then calling the default constructor
12.1/7 A non-user-provided default constructor for a class ... performs the set of
initializations of the class that would be performed by a user-written default
constructor for that class with no ctor-initializer (12.6.2) and an empty
compound-statement.
12.6/4 If a given non-static data member or base class is not named by ... in
initialiser list ... the entity is not initialized.
however, remember effective C++, if you declare a dtor then you should also declare a copy ctor, ctor and self-assignment (even if you declare as private)
Don't work around it, attack it directly.
You'll need a C++ parser. Using the parser you'll have to track the types of variables which have no been properly assigned within each constructor of the classes. Once you've got that going it's simply a matter of inserting the proper syntax to make initialization happen in a desirable way. It's not a simple script to write but it'll save you loads of headache later.
FIrst step is just getting output from the parse tree to label problems for you:
class Foo{
public:
Foo(int a) : mA(a){}
private:
int mA, mB;
};
you'll want your script producing messages like: In class Foo :: missing initializer mB
Then get brave and transform that output into an instruction set to traverse the parse tree and insert missing items so that your code looks like:
class Foo{
public:
Foo(int a) : mA(a), mB(0){}
private:
int mA, mB;
};
So my best advice is to not try to work around the issue, attack it directly. If you still have problems after that, grab your nearest static code analyzer and memory analyzer. Those are the tools that will help you best of all.
Are you sure you (the people purportedly citing the ISO C++ Standard) are actually citing it? And not being confused with the text for updated versions specified in TR's which are not actually Standards? Or C++11 which probably will be soon, but one can hardly expect MS to conform to something not yet officially a Standard.