Question
Should I use [[maybe_unused]] attribute on unused class *tors?
Example
Let's consider the following example:
public: struct keyData{
public: [[maybe_unused]] keyData(){};
public: keyData(::std::chrono::steady_clock::time_point timestamp)
: lastMod(timestamp)
{};
protected: ::std::chrono::steady_clock::time_point lastMod = ::std::chrono::steady_clock::now();
};
I want to init multiple keyDatas using cached time_point timestamps. However, I also provide argumentless constructor that inits keyData, setting lastMod to now() as timestamp was not provided.
Should I mark unused public: keyData(){}; with [[maybe_unused]] argument, as in example code, or not?
Research
[[maybe_unused]] description of Standard attributes site says (bold is mine):
Appears in the declaration of a class, a typedef, a variable, a nonstatic data member, a function, an enumeration, or an enumerator. If the compiler issues warnings on unused entities, that warning is suppressed for any entity declared maybe_unused.
Constructors and member initializer
lists
site says (bold is mine):
Constructor is a special non-static member function of a class that is used to initialize objects of its class type.
Destructors
site says (bold is mine):
A destructor is a special member function that is called when the lifetime of an object ends.
operator overloading
site says (bold is mine):
Overloaded operators are functions with special function names.
So if I understood well, *tors are functions, and as functions they can be marked as [[maybe_unused]], can't they?
Should I use [[maybe_unused]] attribute on unused class *tors?
The purpose of [[maybe_unused]] is to prevent the emission of warnings for compilers that might warn about the qualified item being unused. Does your compiler warn about unused constructors? If so, then you should use it to suppress that warning.
But since most compilers don't warn about disused functions, it's best not to bother. The attribute would just be taking up space.
Unless you're advertising your library as being -Wunused-member-function clean, it's probably best for all involved that you not suppress this warning. After all, users who want to use that warning actually want the warning to be there. And users who don't use the warning don't need [[maybe_unused]] to be sprinkled around seemingly at random.
Related
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.
The specification does not seem to put any constraints on the member functions of a literal class type
I have two questions regarding this
Q1) Do I have complete liberty over what member functions I can put in?
Q2) How do I verify if a class is a literal type? (Possible method: Define a constexpr object of it and check if it compiles?)
The only constraints on literal classes I see are:
• All the data members must have literal type.
• The class must have at least one constexpr constructor.
• If a data member has an in-class initializer, the initializer for a member of built-in type must be a constant expression, or if the member has class type, the initializer must use the member’s own constexpr constructor.
• The class must use default definition for its destructor, which is the member that destroys objects of the class type
(Source: C++ Primer, 5th edition)
Q1. Yes, you can have any methods you like (excluding constructor/destructor which have constraints). Even including virtual methods, if the constructor is constexpr.
Q2. As you say, define a constexpr variable of that type. If there is no diagnostic message (and the compiler is conforming) then the type is definitely a LiteralType. Note that it is possible for the type to be literal but the code to fail compilation for some other reason.
The definition in the Standard seems slightly clearer to me than your quoted definition. For example, there are some cases where a constexpr constructor is not required (e.g. a closure or an aggregate).
I have come across different macros which enable deprecation of member attributes and functions but none of them discuss the possibility of deprecating static class members.
I read that even in C++14 deprecation of static members is not allowed.
Is there any specific reason for this?
Let's get some facts:
The wording for the [[deprecated]] attribute is the following
(based on N4269 7.6.5 [dcl.attr.deprecated], emphasis mine):
The attribute may be applied to the declaration of a class, a typedef-name, a variable, a non-static data
member, a function, a namespace, an enumeration, an enumerator, or a template specialization.
The misleading part is the explicit appearance of "non-static data members" without its counterpart at the same level in this list, but in that same list there are two other elements of interest.
The description of a variable is (based on 3 [basic]) :
A variable is introduced by the declaration of a reference other than a non-static data member or of an
object. The variable’s name, if any, denotes the reference or object.
Which means that saying a variable includes static data members
A static member function is a function (a red car is a car, this is a logical conclusion and i don't seem to find anything counter indicating this in the standard).
There isn't any syntax or behaviour problem applying particularly to static members, a static function is pretty much a free function in a namespace and static data member is more or less a global variable in a namespace and you can deprecate free functions and global variables..
As a bonus, it actually works in major compilers.
Summing up all this facts basically means that the [[deprecated]] attribute may actually be applied to a static data member, a non-static data member and a static member function, among other things.
So to answer your question, from my understanding, deprecating static members is actually allowed by the standard.
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. :)
I've been working on some C++ code that a friend has written and I get the following error that I have never seen before when compiling with gcc4.6:
error: use of deleted function
‘GameFSM_<std::array<C, 2ul> >::hdealt::hdealt()’ is implicitly deleted because the default definition would be ill-formed:
uninitialized non-static const member ‘const h_t FlopPokerGameFSM_<std::array<C, 2ul> >::hdealt::h’
Edit: This comes from a part of the code using boost MSM: Boost Webpage
Edit2: There is no = delete() used anywhere in the sourcecode.
Generally speaking, what does this error mean? What should I be looking for when this type of error occurs?
The error message clearly says that the default constructor has been deleted implicitly. It even says why: the class contains a non-static, const variable, which would not be initialized by the default ctor.
class X {
const int x;
};
Since X::x is const, it must be initialized -- but a default ctor wouldn't normally initialize it (because it's a POD type). Therefore, to get a default ctor, you need to define one yourself (and it must initialize x). You can get the same kind of situation with a member that's a reference:
class X {
whatever &x;
};
It's probably worth noting that both of these will also disable implicit creation of an assignment operator as well, for essentially the same reason. The implicit assignment operator normally does members-wise assignment, but with a const member or reference member, it can't do that because the member can't be assigned. To make assignment work, you need to write your own assignment operator.
This is why a const member should typically be static -- when you do an assignment, you can't assign the const member anyway. In a typical case all your instances are going to have the same value so they might as well share access to a single variable instead of having lots of copies of a variable that will all have the same value.
It is possible, of course, to create instances with different values though -- you (for example) pass a value when you create the object, so two different objects can have two different values. If, however, you try to do something like swapping them, the const member will retain its original value instead of being swapped.
You are using a function, which is marked as deleted.
Eg:
int doSomething( int ) = delete;
The =delete is a new feature of C++0x. It means the compiler should immediately stop compiling and complain "this function is deleted" once the user use such function.
If you see this error, you should check the function declaration for =delete.
To know more about this new feature introduced in C++0x, check this out.
I encountered this error when inheriting from an abstract class and not implementing all of the pure virtual methods in my subclass.
gcc 4.6 supports a new feature of deleted functions, where you can write
hdealt() = delete;
to disable the default constructor.
Here the compiler has obviously seen that a default constructor can not be generated, and =delete'd it for you.
In the current C++0x standard you can explicitly disable default constructors with the delete syntax, e.g.
MyClass() = delete;
Gcc 4.6 is the first version to support this syntax, so maybe that is the problem...
Switching from gcc 4.6 to gcc 4.8 resolved this for me.