Code looks like:
struct Foo {
Foo(const char *);
};
Foo::Foo(const char *str = 0)
{
}
VS 2013 and gcc 4.8.0 accept such code,
while clang 3.3 reject such code with:
error: addition of default argument on redeclaration makes this constructor a default constructor
who is right from standard (C++03 and C++11) point of view?
Note:
I like clang's choice too, but I going to report bug to gcc and visual studio,
and if this is not correct from standard point of view, this helps to
convince compiler's developers to fix this issue.
GCC
I described issue here: http://gcc.gnu.org/bugzilla/show_bug.cgi?id=58194
But no luck, they suspend bug fixing untill draft become standard.
This has been discussed on the Clang mailinglist and has been submitted as a Defect Report Core Issue 1344.
From the mailinglist discussion:
The idea is that the presence of certain special members affects core
properties of a class type, like whether it's POD or trivially
copyable. Deciding these properties should not require whole-program
knowledge; it's important for us to be able to deduce them just from
the class definition. The really problematic case is turning a
"normal" constructor into a copy or move constructor by adding default
arguments, but IIRC introducing a default constructor was also
problematic.
The fix is that you should put the default argument in the initial
declaration of the constructor.
This was last discussed by WG21 at the Bloomington meeting. Notes from
there:
"Consensus: Make this ill-formed as suggested in the write-up. Core
issue 1344. Priority 0, Doug drafting."
So CWG has agreed (in principle) that this should be ill-formed.
TL;DR Clang is right whenever the defect gets fixed (not sure if that can officially only happen with C++14, or if such Committee decisions can also be done retroactively on C++11)
I would say CLANG is right. The standard says (12.1.5 for the both old and new versions of the standard):
A default constructor for a class X is a constructor of class X that can be called without an argument
Adding the default value to the only argument of the constructor definitely makes it possible to call it without arguments, thus making it a default one. Also, 8.3.6 says (emphasis mine):
A default argument expression shall be specified only in the
parameter-declaration-clause
of a function declaration <...>
You have a declaration and a definition. In your declaration you do not have a default value, while in your definition you have a default value. In fact the signature of the declaration is very similar to the signature of the definition, but not the same. I believe that strictness is a good idea, so I believe it is better to enforce that the declaration is the same as the definition.
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.
Let's say, for reasons of my own, I want a class to have a non-static reference member. I would think that this type should be easily optimized out of most code that uses it. Therefore, I assert its triviality in a unit test.
Clang and GCC agree that the class is trivial, but MSVC disagrees. Who is right, and why, according to the standard?
#include <type_traits>
struct example {
int& r;
};
// Clang and GCC let this pass
// MSVC fires this assertion
static_assert(
std::is_trivial<example>::value,
"example is not trivial"
);
According to C++17 [class]/6, for a class to be trivial, among other requirements, it has to have at least one non-deleted default constructor. The example class's default constructor is deleted, so example is not a trivial class.
Before C++17, the situation is somewhat less clear. It was required for a trivial class to have a trivial default constructor, and it was not clear whether a deleted default constructor qualifies as trivial. Richard Smith asked in CWG 1928 whether special member functions that are defaulted and implicitly deleted are trivial. The Committee's response was:
CWG feels that the triviality of a deleted function should be irrelevant. Any cases in which the triviality of a deleted function is observable should be amended to remove that dependency.
Subsequently, the resolution of CWG 1496 resolved this issue in the case of trivial classes: it no longer matters whether a deleted default constructor qualifies as trivial, because in C++17, the definition says that all default constructors (in case there are multiple) must be either trivial or deleted and at least one must be non-deleted.
It seems that GCC and Clang may have been considering deleted default constructors as trivial, and haven't been updated with the C++17 definition of "trivial class". You should file bug reports.
Using ReSharper with C++17, and I enabled many of the warnings just to see what my project warns me about. I get this:
Declaring a parameter with a default argument is disallowed[fuchsia-default-arguments]
The code in question is the constructor:
class Point2D
{
public:
explicit Point2D(double x = 0.0, double y = 0.0);
};
I'm wondering why default arguments would be considered bad/poor/worthy of a warning? Does anyone have any code examples proving this a viable warning?
Here is the documentation.
There are several odd corner cases when it comes to default arguments on function parameters.
Here is a presentation from CppCon 2017 detailing a lot of tricky behavior. https://youtu.be/NeJ85q1qddQ
To summarize the main points:
Redeclarations cause potentially confusing behavior
The default arguments used in virtual function calls are not determined by the function called, but by the static type.
Default arguments on function templates can potentially look ill-formed, but may compile as long as they are not instantiated.
Of course, for your case of a non-template constructor, they are fairly innocuous. It can’t be overridden or redeclared (although an out-of-line definition might cause you possible pain).
The following code does not compile in GCC 4.7.2 or Clang 3.2:
#include <vector>
#include <functional>
int main()
{
std::vector<std::function<void()>> a;
std::vector<std::function<void()>> b{a};
}
The issue is that the compiler will try to create b using an initializer_list, when clearly it should just be calling the copy constructor. However this seems to be desired behavior because the standard says that initializer_list constructors should take precedence.
This code would work fine for other std::vector, but for a std::function the compiler can't know whether you want the initializer_list constructor or another one.
It doesn't seem like there is a way around it, and if that is the case then you can never use uniform initialization in templated code. Which would be a giant shame.
Visual Studio (2012 November CTP) on the other hand doesn't complain about this. But the initializer_list support is not very good in there at the moment, so it might be a bug.
This is LWG 2132 which is not yet a Defect Report but there's clear consensus (and implementation experience) to fix it. The standard says that std::function's constructor will accept any type, so because an initializer-list constructor is always preferred to other constructors if it's viable, your code tries to construct a vector from an std::initializer_list<std::function<void()>> with a single element initialized from the object a. That then causes an error because although you can construct a std::function<void()> from a the resulting object isn't callable.
In other words the issue is that std::function has an unconstrained template constructor allowing conversion from any type. That causes a problem in your case because initializer-list constructors are preferred to other constructors if viable, and the unconstrained function constructor means it's always possible to create an initializer_list<function<void()>> from any type so an initializer-list constructor is always viable.
The proposed resolution to 2132 prevents constructing a std::function from a non-callable type, so the initializer-list constructor isn't viable and the vector copy constructor is called instead. I implemented that resolution for GCC 4.8, and it's already implemented in Clang's libc++ library too.
I can't see any reason why this shouldn't compile and both gcc (version 4.8.0 20121111) and clang (version 3.3 (trunk 171007)) compile the code. That said, "uniform initialization" is far from uniform: There are definitely cases where you can't use braces when invoking a constructor.
I tested on two compilers, and was surprised to see both support the following definition without complaint:
class A {
A();
};
A::A::A() {}
^^^
Note that this also succeeds for methods, although it is flagged when the declaration is over-qualified.
Questions:
Is this a valid C++ program?
If so, what purpose does it serve - or is it merely a byproduct?
Updated Detail:
In case the original question was not clear or too short: I'm curious why redundant qualifications are permitted on the definition (emphasis also added above).
Clang an Apple's GCC 4.2 + LLVM were the compilers
Yes, it's allowed (§9/2):
The class-name is also inserted into the scope of the class itself; this is known as the injected-class-name. For purposes of access checking, the injected-class-name is treated as if it were a public member name.
For information about the reasoning that lead to class name inject, you might want to read N0444.