In cppreference, function declaration, under the section User-provided functions. There is a sentence:
Declaring a function as defaulted after its first declaration can provide efficient execution and concise definition while enabling a stable binary interface to an evolving code base.
The main question is =default and =delete is a function declaration or a function definition?
Shoudn't that be
Defining a function as defaulted after its first declaration?
The main question is =default and =delete is a function declaration or a function definition?
As per [dcl.fct.def.delete]/1 and [dcl.fct.def.default]/1 they are definitions. However definitions are also declarations whilst not all declarations are definitions.
Shouldn't that be
Defining a function as defaulted after its first declaration?
As per above both 'declaring' and 'defining' are formally correct, but indeed, using 'defining' could arguably be more non-formally precise.
Finally note that there is key difference between explicitly deleted and explicitly defaulted functions in the context of this question: a deleted function shall be the first declaration of the function (except for deleting explicit specializations of function templates - deletion should be at the first declaration of the specialization), meaning you cannot declare a function and later delete it, say, at its definition local to a translation unit; as per [dcl.fct.def.delete]/4:
A deleted function is implicitly an inline function ([dcl.inline]).
[Note 2: The one-definition rule ([basic.def.odr]) applies to deleted
definitions. — end note]
A deleted definition of a function shall be the first declaration of
the function or, for an explicit specialization of a function
template, the first declaration of that specialization. An implicitly
declared allocation or deallocation function ([basic.stc.dynamic])
shall not be defined as deleted.
[Example 4:
struct sometype {
sometype();
};
sometype::sometype() = delete; // error: not first declaration
— end example]
Those are definitions. But like most (all?) definitions, they're also declarations.
[dcl.fct.def.delete]/1:
A deleted definition of a function is a function definition whose ...
[dcl.fct.def.default]/1:
A function definition whose function-body is of the form = default ; is called an explicitly-defaulted definition ...
In the same sense we can also argue that the term declaring as defaulted should be transformed into defining as defaulted, because when we declare a function as defaulted, it actually generates the definition of the function.
So here
struct triv
{
triv() = default;
};
We at the same time declare it's default constructor, and henceforth define it. I think it is called declaring as defaulted, because of syntax being similar more to a declaration of a function than a definition of a function.
Also in C++11 standard, in 8.4.2.4. It is noted that
A user-provided explicitly-defaulted function (i.e., explicitly defaulted after its
first declaration) is defined at the point where it is explicitly defaulted.
So I think it is redundant to search for a difference in terms defaulting a function, declaring as defaulted and defining as defaulted.
Related
In C++, you can declare many things as constexpr: variables, functions (including member functions and operators), constructors, and since C++1z, also if statements and lambda expressions. However, declaring a destructor constexpr results in an error:
struct X {
constexpr ~X() = default; // error: a destructor cannot be 'constexpr'
};
My questions:
Why can't a destructor be marked constexpr?
If I do not provide a destructor, is the implicitly generated destructor constexpr?
If I declare a defaulted destructor (~X() = default;), is it automatically constexpr?
As per the draft basic.types#10 possibly cv-qualified class type that has all of the following properties:
A possibly cv-qualified class type that has all of the following properties:
(10.5.1) - it has a trivial destructor,
(10.5.2) - it is either a closure type, an aggregate type, or has at
least one constexpr constructor or constructor template (possibly
inherited from a base class) that is not a copy or move constructor,
(10.5.3) - if it is a union, at least one of its non-static data
members is of non-volatile literal type
(10.5.4) - if it is not
a union, all of its non-static data members and base classes are of
non-volatile literal types.
Ques 1: Why a destructor cannot be marked as constexpr?
Because only trivial destructors are qualified for constexpr
Following is the relevant section of the draft
A destructor is trivial if it is not user-provided and if:
(5.4) — the destructor is not virtual,
(5.5) — all of the direct base classes of its class have trivial
destructors, and
(5.6) — for all of the non-static data members of its class that are
of class type (or array thereof), each such class has a trivial
destructor.
Otherwise, the destructor is non-trivial.
Ques 2: If I do not provide a destructor, is the implicitly generated destructor constexpr?
Yes, because implicitly generated destructor is trivial type, so it is qualified for constexpr
Ques 3: If I declare a defaulted destructor (~X() = default;), is it automatically constexpr?
Indeed, this destructor is user-declared and implicitly-generated and thus it is qualified for constexpr.
I'm not able to find any direct reference that only trivial destructors are qualified for constexpr but if the destructor is not trivial then it is for sure that class type is not cv-qualified. So it kind of implicit as you can't define a destructor for cv-qualified class.
C++20 Update
Since C++20, user defined destructors can also be constexpr under certain conditions.
dcl.constexpr/3:
The definition of a constexpr function shall satisfy the following
requirements:
its return type (if any) shall be a literal type;
each of its parameter types shall be a literal type;
it shall not be a coroutine ([dcl.fct.def.coroutine]);
if the function is a constructor or destructor, its class shall not have any
virtual base classes;
its function-body shall not enclose ([stmt.pre])
a goto statement,
an identifier label ([stmt.label]),
a definition of a variable of non-literal type or of static or thread
storage duration.
If what you're looking for is reasoning behind the restriction, have a look at this paper which clearly states that the restriction is artificial - there is no intrinsic property of destructors that prevent them from working in constexpr contexts, and indeed compiler implementors agree that supporting them in constexpr contexts will be trivial to implement.
I guess the C++ standards committee originally placed the restriction in C++11 because they didn't want to deal with destructors at that time and it was easier to just rule them out entirely.
Since C++20, a constructor may be marked constexpr; I don’t know if it says anywhere specifically “a destructor may be constexpr”, but the draft standard includes the following text in section 9.2.5 paragraph 5:
The definition of a constexpr destructor whose function-body is not = delete shall additionally satisfy the
following requirement:
for every subobject of class type or (possibly multi-dimensional) array thereof, that class type shall
have a constexpr destructor.
This also now has a useful function because C++20 also allows new and delete in constexpr contexts, allowing things like vector and string to work at compile time without hacks (although I believe C++20 does not actually include changes to the standard library to allow for this, it is possible to implement something with the same API and behaviour as vector that works completely at compile time).
Why a destructor cannot be marked as constexpr?
The C++11 standard is specific about use of constexpr for consructors and non-static member function. It does not say anything specific about destructor. One may assume that destructors are to be treated as non-static member functions.
constexpr can be used only for const member functions. Since a destructor cannot be const member function, it cannot be qualified as a constexpr member function.
If I do not provide a destructor, is the implicitly generated destructor constexpr.
Since use of
constexpr ~X() = default;
is an error, it makes sense to me that the compiler generated destructor is not a constexpr function. I can't find anything in the standard to justify my statement. I am guessing.
If I declare a defaulted destructor (~X() = default;), is it automatically constexpr
I think not. Once again, I can't find anything in the standard to justify my statement. I am guessing.
FWIW, g++ compiles and builds the following program just fine.
struct X {
constexpr X(int i) : i_(i) {}
~X() = default;
int i_;
};
int main()
{
const X x(10);
}
Reference say's:
constexpr destructors
In most cases, in order to create an object of a type T in a constant
expression, the destruction of T must be trivial. However, non-trivial
destructors are an important component of modern C++, partly due to
widespread usage of the RAII idiom, which is also applicable in
constexpr evaluations. Non-trivial destructors could be supported in
constant expressions, as follows:
Allow destructors to be marked as constexpr
Make defaulted destructors constexpr if they only invoke constexpr destructors
For constexpr variables, require that evaluating the destructor is a constant expression (except that the object being destroyed may be
modified in its own destructor
However, no compelling use cases are known for such a feature, and
there would be a non-trivial implementation cost ensuring that
destructors are run at the right times.
A destructor can't be constexpr because constexpr functions can't have side effects and destructors by definition are only useful through side effects. In short, it would be useless to have a destructor that is constexpr.
A object cannot be constexpr if its destructor is non-trivial. A defaulted one, if trivial, will be considered constexpr
Live
From [class.dtor]
Each decl-specifier of the decl-specifier-seq of a destructor declaration (if any) shall be friend, inline, or virtual.
Missing from it, constexpr. So you could just take it as: because the standard says soTM
I got an official answer to this question that decltype should not trigger function compilation. In fact decltype on a function that is declared but not defined is legal.
Next question, should taking the address of a function trigger the compilation of a function? Take this example:
template <typename T>
void foo(T&& x) { x.func(); }
int main()
{
auto bar = &foo<int>;
}
All the compilers I've tested fail with an error like:
Request for member func in x, which is of non-class type int
But if I just define foo and don't declare it, the code compiles fine. Can someone provide me with an official source on whether taking the address of a function should require it's compilation?
3.2/2:
An expression is potentially evaluated unless it is an unevaluated
operand (Clause 5) or a subexpression thereof. ... A non-overloaded
function whose name appears as a potentially-evaluated expression or a
member of a set of candidate functions, if selected by overload
resolution when referred to from a potentially-evaluated expression,
is odr-used, unless it is a pure virtual function and its name is not
explicitly qualified.
Then 3.2/3:
Every program shall contain exactly one definition of every non-inline
function or variable that is odr-used in that program; no diagnostic
required. The definition can appear explicitly in the program, it can
be found in the standard or a user-defined library, or (when
appropriate) it is implicitly defined (see 12.1, 12.4 and
12.8). An inline function shall be defined in every translation unit in which it is odr-used.
The function name is definitely not an unevaluated operand (for example to sizeof, decltype), AND it appears in an expression, so it's potentially evaluated. Then the second one requires exactly one non-inline definition, or identical inline definitions in each translation unit.
I have a static_assert in a move constructor of a template struct of mine. Is this static_assert required to be considered by the compiler, even if copy elision is possible?
This is the stripped-down scenario:
#include <type_traits>
template<typename T>
struct X
{
X(X&&) { static_assert(std::is_same<void, T>::value, "Intentional Failure"); }
};
auto impl() -> X<int>;
auto test() -> decltype(impl())
{
return impl();
}
int main()
{
test();
}
GCC and Clang agree to evaluate the static_assert and fail to compile.
MSCV and ICC on the other hand compile the code just fine.
Interestingly, when I remove the definition of the move constructor and just declare it like this:
template<typename T>
struct X
{
X(X&&);
};
GCC and Clang also compile the code now. Thus, all compilers seem to agree that the definition of the move constructor is irrelevant for copy elision.
Question:
If there is a static_assert in the copy/move constructor, does the standard require it to be evaluated even if copy/move elision is possible?
The following should help.
You do not have to employ type deduction to illustrate the problem. Even the simpler example has the same issue:
#include <type_traits>
template <typename T>
struct X
{
X() {}
X(X&&) { static_assert(std::is_same<void, T>::value, "failed"); }
};
int main()
{
X<int> x = X<int>();
}
Clang and GCC will not compile it. MSVC compiles and executes fine.
This shows that the problem is related to odr-use and when definitions of member functions are instantiated.
14.7.1 [temp.inst] paragraph 2 says "[...] the specialization of the member is implicitly instantiated when the specialization is referenced in a context that requires the member definition to exist"
3.2 [basic.def.odr] paragraph 3 says (in a note) "[...] A constructor selected to copy or move an object of class type is odr-used even if the call
is actually elided by the implementation"
3.2 [basic.def.odr] paragraph 4 says "Every program shall contain exactly one definition of every non-inline function or variable that is odr-used in that program; no diagnostic required."
Ergo: the specialization should be instantiated, and the assertion have fired.
I believe the answer is no. My logic goes like this:
Copy elision requires declaration of copy/move constructors but doesn't require definition.
Member function definitions of templates are not instantiated unless their definitions are required.
If a definition is not instantiated it cannot be tested for being ill-formed.
References:
14.7.1.1 …The implicit instantiation of a class template specialization causes the implicit instantiation of the declarations, but not of the definitions, default arguments, or exception-specifications of the class member functions…
14.7.1.2 Unless a member of a class template… has been explicitly instantiated or explicitly specialized, the specialization of the member is implicitly instantiated when the specialization is referenced in a context that requires the member definition to exist…
move constructors are not called. static_assert is evaluated upon instantiation of X<int>::X(X&&). Most probably, some compilers evaluate template methods upon use (when you use move constructor, and you don't use it), and others - upon instantiation of class template (when you first use X<int>).
I think the answer is : yes.
first the static_assert forces the constructor from "definition" to a declaration.
I am not sure exactly about the template nature of the static_assert with regard to the the 12.8 section below either...
(I apologize for the formatting...)
c
©
ISO/IEC
N3242=11-0012
7 Declarations [dcl.dcl]
2.
A declaration is a definition unless it declares a function without specifying the function’s body (8.4), it contains the extern specifier (7.1.1) or a linkage-specification 25(7.5) and neither an initializer nor a function-body, it declares a static data member in a class definition (9.4), it is a class name declaration (9.1), it is an opaque-enum-declaration(7.2), or it is a typedef declaration (7.1.3), a using-declaration(7.3.3), a static_assert-declaration(Clause 7), an attribute-declaration (Clause 7), an empty-declaration (Clause 7), or a using-directive (7.3.4)
12.8 Copying and moving class objects [class.copy]
7 A member function template is never instantiated to perform the copy of a class object to an object of its class type. [Example:
struct S {
template<typename T> S(T);
template<typename T> S(T&&);
S();
};
S f();
const S g;
void h() {
S a( f() );// does not instantiate member template;
// uses the implicitly generated move constructor
S b(g);// does not instantiate the member template;
// uses the implicitly generated copy constructor
}
— end example
]
32 When certain criteria are met, an implementation is allowed to omit the copy/move construction of a class object, even if the copy/move constructor and/or destructor for the object have side effects. In such cases, the implementation treats the source and target of the omitted copy/move operation as simply two different ways of referring to the same object, and the destruction of that object occurs at the later of the times when the two objects would have been destroyed without the optimization.
123 - This elision of copy/move operations, called copy elision, is permitted in the following circumstances (which may be combined to eliminate multiple copies):
— in a return statement in a function with a class return type, when the expression is the name of a non-volatile automatic object (other than a function or catch-clause parameter) with the same cv-unqualified type as the function return type, the copy/move operation can be omitted by constructing the automatic object directly into the function’s return value
I recently found out that the types of parameters in a non-defining function declaration may be of incomplete types. This is very exciting.
class A;
class B {
B(A a); // Legal! Wow!
};
The type is required to be complete only for the definition:
B::B(A a) {}; // error: ‘a’ has incomplete type
I've been trying to pin down the legalese for this, but my searches through C++11 for "[in]complete type" have yielded nothing of much interest, leading me to assume that these semantics are defined through an enigmatic maze of constructions.
Can you help me pin down the standard text that defines the above requirements for the types of function parameters being complete or otherwise, in function declarations vs definitions?
(9.2/10 and 9.4.2/2 give us the requirements for static data member declarations and non-static data member definitions in class definitions.)
See 8.3.5p9, which lays down the exact rules. For a = delete definition, implementations are likely to accept incomplete parameter types too, retroactively (as was determined in a DR resolution by the C++ committee).
In particular, there is no action done on parameters or return values in a non-defining function declaration. Copying of arguments to parameters is done in the context of the caller. And destruction of parameters is done in the context of the callee, in the function definition. Destruction of the return value is done in the context of the caller in a function call except if the call is the topmost expression or right operand of a topmost comma operator in a decltype. Then no destruction happens because no temporary is created as a special case (to help SFINAE libraries).
Function declaration
There doesn't appear to be anything directly addressing this. It may be that it's allowed because it is not disallowed.
7.1.1/9 tells us that it's ok for an extern declaration (which is semantically similar to a member function declaration), and shows us non-normatively that types in such declarations may be incomplete:
[C++11: 7.1.1/9]: The name of a declared but undefined class can be used in an extern declaration. Such a declaration can only be used in ways that do not require a complete class type. [ Example:
struct S;
extern S a;
extern S f();
extern void g(S);
void h() {
g(a); // error: S is incomplete
f(); // error: S is incomplete
}
—end example ]
Function definition (thanks litb)
[C++11: 8.3.5/9]: Types shall not be defined in return or parameter types. The type of a parameter or the return type for a function definition shall not be an incomplete class type (possibly cv-qualified) unless the function definition is nested within the member-specification for that class (including definitions in nested classes defined within
the class).
Function call
[C++11: 5.2.2/4]: [..] When a function is called, the parameters that have object type shall have completely-defined object type. [ Note: this still allows a parameter to be a pointer or reference to an incomplete class type. However, it prevents a passed-by-value parameter to have an incomplete class type. —end note ] [..]
The C++ Language Standard states the following concerning template components in the Standard Library:
The effects are undefined...if an incomplete type is used as a template argument when instantiating a template component, unless specifically allowed for that component (C++11 §17.6.4.8/2).
Does the following cause instantiation of the std::vector class template?
class X;
std::vector<X> f(); // Declaration only; we will define it when X is complete
To ask it another way, in the function declaration std::vector<X> f();, is std::vector instantiated with the argument X? Or, is std::vector<X> not instantiated until f() is odr-used or defined?
Likewise, does the following cause instantiation of the std::vector class template?
class X;
typedef std::vector<X> XVector; // We will complete X before we use XVector
While I use std::vector in these examples, the question applies equally to all templates.
§ 14.7.1\1 Implicit instantiation [temp.inst]
Unless a class template specialization has been explicitly
instantiated (14.7.2) or explicitly specialized (14.7.3), the class
template specialization is implicitly instantiated when the
specialization is referenced in a context that requires a
completely-defined object type or when the completeness of the class
type affects the semantics of the program. The implicit instantiation
of a class template specialization causes the implicit instantiation
of the declarations, but not of the definitions or default arguments,
of the class member functions, member classes, static data members and
member templates; and it causes the implicit instantiation of the
definitions of member anonymous unions. Unless a member of a class
template or a member template has been explicitly instantiated or
explicitly specialized, the specialization of the member is implicitly
instantiated when the specialization is referenced in a context that
requires the member definition to exist; in particular, the
initialization (and any associated side-effects) of a static data
member does not occur unless the static data member is itself used in
a way that requires the definition of the static data member to exist.
§ 8.3.5\9 Functions [dcl.fct]
Types shall not be defined in return or parameter types. The type of a
parameter or the return type for a function definition shall not be an
incomplete class type (possibly cv-qualified) unless the function
definition is nested within the member-specification for that class
(including definitions in nested classes defined within the class).
§ 3.1\2 Declarations and definitions [basic.def]
A declaration is a definition unless it declares a function without
specifying the function’s body (8.4), it contains the extern specifier
(7.1.1) or a linkage-specification25 (7.5) and neither an initializer
nor a function-body, it declares a static data member in a class
definition (9.4), it is a class name declaration (9.1), it is an
opaque-enum-declaration (7.2), or it is a typedef declaration (7.1.3),
a using-declaration (7.3.3), a static_assert-declaration (Clause 7),
an attribute-declaration (Clause 7), an empty-declaration (Clause 7),
or a using-directive (7.3.4).
It's only instantiated if it's required. I couldn't find a clear definition anywhere, but the second quote says that those declaratations are not definitions, which seems to be the same to me.
No, it does not instantiate the template. Mooing Duck's answer provides all the necessary quotes, but here is some analysis.
Instantiation, by default, cannot occur if nothing exists to require a completely-defined type (§14.7.1/1). Function definitions specifically require complete types (§8.3.5/9), but the question is whether some other part of the standard also requires this for other declarations.
But there's a special exception for definitions, which reveals that non-definition declarations really are different:
The type of a parameter or the return type for a function definition shall not be an incomplete class type (possibly cv-qualified) unless the function definition is nested within the member-specification for that class (including definitions in nested classes defined within the class).
What's special about function definitions inside member-specifications? Because a member-specification cannot declare the same function twice (§9.2/1), and member function bodies are processed after all member declarations (§3.3.7/1.1). Essentially, a nested member function definition is treated as a declaration during the first pass, and then a definition once the entire member-specification has been processed, and the class is complete (§9.2/2). And §8.3.5/9 specifies that an incomplete class is permissible for that first pass, but not the second.
It's pretty onerous to perform an exhaustive, definitive search of the Standard's rules for function declarations and instantiations. But this example, although limited to member functions and the completeness of the enclosing type, can reasonably be extended to other functions and types. In any case, it's pretty good evidence of a distinction.