Literal class type member function constraints - c++

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).

Related

std::is_constructible for aggregates with invalid default initializer

What std::is_constructible shall return for an aggregate type that does not allow creation of objects due to invalid initializer of a member field?
Consider for example
#include <type_traits>
template <class T>
struct A {
T x{};
};
static_assert( !std::is_constructible_v<A<int&>> );
A<int&> obj; is not well-formed since it cannot initialize int& from {}. So I would expect that the example program above compiles fine, as it does in GCC. But MSVC accepts the opposite statement static_assert( std::is_constructible_v<A<int&>> ); presumable since the default constructor of A was not formally deleted. And Clang behaves in the third way stopping the compilation with the error:
error: non-const lvalue reference to type 'int' cannot bind to an initializer list temporary
T x{};
^~
: note: in instantiation of default member initializer 'A<int &>::x' requested here
struct A {
^
Online demo: https://gcc.godbolt.org/z/nnxcGn7WG
Which one of the behaviors is correct according to the standard?
std::is_constructible_v<A<int&>> checks whether a variable initialization with () initializer is possible. That's never aggregate initialization (not even in C++20), but always value initialization, which will use the default constructor.
Because your class doesn't declare any constructor, it still has an implicit default constructor declared. That one will be called during value initialization.
The implicit default constructor is also not defined as deleted, because none of the points in [class.default.ctor]/2 apply. There is one point for reference members without any default member initializer though.
So this constructor will be chosen in overload resolution and therefore std::is_constructible_v<A<int&>> is true. That the instantiation of the default constructor might be ill-formed is irrelevant. Only declarations are checked (the immediate context). So GCC's behavior is not correct.
The only remaining question now is whether the implicit default constructor (or rather the default member initializer?) is supposed to be instantiated from the std::is_constructible test itself, causing the program to be ill-formed.
As far as I can tell the standard doesn't specify that clearly. The standard says that the noexcept-specifier of a function is implicitly instantiated when it is needed. (see [temp.inst]/15)
It also says that it is needed if it is used in an unevaluated operand in a way that would be an ODR use if it was potentially-evaluated. (see [except.spec]/13.2
Arguably the type trait has to make such a use.
Then [except.spec]/7.3 specifies that it needs to be checked whether the default member initializers are potentially-throwing in order to check whether the implicit default constructor has a potentially-throwing exception specification.
Clang seems to then follow the idea that this requires instantiation of the default member initializer and therefore causes the compilation error because that instantiation is invalid.
The problems I see with that is:
I don't see anything in the [temp.inst] about when default member initializers are instantiated or what that would mean exactly.
[temp.inst]/15 speaks of the noexcept-specifier grammar construct and since the default constructor is implicit, that doesn't really work out.
The standard states:
Only the validity of the immediate context of the variable initialization is considered.
and
The evaluation of the initialization can result in side effects such as the instantiation of class template specializations and function template specializations, the generation of implicitly-defined functions, and so on.
Such side effects are not in the “immediate context” and can result in the program being ill-formed.
It is the generation of an the definition for an implicity-defined function (the constructor) that fails in your example. So this is not the "immediate context". That would allow:
The msvc interpretation: It doesn't consider the validity of the non-immediate context
The clang interpretation: The side effects result in your program being ill-formed
If anything it seems to me that gcc's result might be incorrect. But I'm not sure if the standard wants to forbid the consideration of the "non-immediate context" or if it simply doesn't require it.

Does a constructor has a "type" in C++ since it is a special member function

I recently learnt that constructors do not have names in C++ and some other things about them. I am also aware that a function has a type in C++ called a function type. For example,
void func(int)
{
}
In the above snippet the func has the function type void (int).
Now, I want to know that since constructors are special member functions then do they also have a type like the one shown above. For example say we have:
struct Name
{
Name(int)
{
}
};
Does the constructor shown above also has a function type just like ordinary functions or ordinary member functions. If yes, then how can we find that type. Like we can use decltype on ordinary functions, is it permitted to use decltype on constructors to find their type.
is it permitted to use decltype on constructors to find their type
It's not permitted. Primarily because there is no way to name a constructor. A common misnomer is that an expression like Name(0) or new Name(0), calls the constructor. But that isn't the case like in func(0). A constructor is never called by us directly, but rather always indirectly by the language construct that requires a new object to come into being.
[class.ctor.general]
1 ... Constructors do not have names.
2 A constructor is used to initialize objects of its class type. Because constructors do not have names, they are never found during name lookup; however an explicit type conversion using the functional notation ([expr.type.conv]) will cause a constructor to be called to initialize an object.
[Note 1: The syntax looks like an explicit call of the constructor. — end note]
Because we cannot name them, we cannot use introspection mechanisms like decltype to examine them. Therefore the standard doesn't specify a "type" for constructors, since there is no way for a strictly standard compliant program to examine said type.
A constructor also cannot possess a signature (as defined by the standard), since that by definition includes the function name (and constructors are, as mentioned, nameless).
[defns.signature.member] signature
⟨class member function⟩ name, parameter-type-list, class of which the function is a member, cv-qualifiers (if any), ref-qualifier (if any), and trailing requires-clause (if any)
A classes constructor is never called explicitly. You instantiate an object using something like new Name(5), memory will be allocated and then some part of that memory may be initialized by the steps defined in the constructors body.
Notice that a constructor has no return statement. What is returned by new Name(5) is a memory reference to the memory allocated by new.
This is given away by syntax like:
Name * foo = new Name(5)
foo is the pointer to whatever has be allocated and type checking can be done because Name referrs to a class, not to its constructor.

Does a constructor also have an implicit this parameter

I am learning about classes in C++ and know that non-static member functions have implicit this parameter. My first question is that does constructor also have an implicit this parameter just like non-static member functions. Note that i am not asking whether we can use this inside a ctor as i already know that we can use this inside a ctor.
Next, I know that inside a const qualified non-static member function for a class X, the type of this is const X*. And for a non-static member function(without const qualified), the type of this is X*. Similarly, inside the ctor the type of this is always X*. Here comes the deeper question.
We know that when we call a non-static member function(say like obj.func()), then the address of the object named obj is implicitly passed to the implicit this parameter of the method func. So this explains "where the this comes from in case of non-static member function".
Now, lets apply the same thing to constructors. For example, say we create an object of class X using default ctor like:
X x; //does the default ctor also have an implicit this parameter to which the address of x is passed?
My second question is: Does the same thing happen to ctors? Like, the address of x is passed to an implicit parameter of the default ctor. My current understanding is that ctors don't have an implicit this parameter. So when we write X x;, the address of x is not passed as an argument because the object is not created yet and so it doesn't make sense to pass its address. But from the standard we know that inside a ctor we can use the this pointer. So my second question essentially is that, if ctors don't have an implicit this parameter then where does the this in the statement this->p = 0; come from? We know that before using any name(like variable name) in C++, we must have a declaration for that name. So where is the declaration for this? Does the compiler implicitly declares the this in case of ctor? I mean, in case of non-static member function, i can understand that they have the declaration for this as the implicit this parameter but what happens in ctors? How inside ctors we're able to use the name this without having a declaration for that?
struct Name
{
private:
int p = 0;
int k = 0;
void func() //func is a non-static member function and so have an implicit this parameter
{
this->k = 0; // the "this" here comes from implicit this parameter
}
Name()
{
this->p = 0; //where does the "this" comes from here since ctor don't have implicit this parameter
}
};
My third question is that is the concept of implicit this parameter an implementation detail or does the standard says that non-static member function will have an implicit this parameter.
Summary
Do ctors have an implicit this parameter? This first question can also be phrased as "Do ctors also have an implicit object parameter?".
The standard says that we can use this inside a ctor. But where does that this come from. For example, in case of a non-static member function, we know that this comes from the implicit this parameter, but in case of ctors, since ctors don't have an implicit this parameter, where does that this that we're allowed to use inside ctor come from.
Is the concept of implicit this parameter an implementation detail or does the standard say that all non-static member functions have an implicit this parameter, in which case, ctors are also allowed by implementations to have an implicit this parameter.
Edit:
The most important part(IMO) of this question is that how are we able to use the name this inside a ctor? For example when we write:
this->p = 0; //here "this" behaves like a name
In the above statement this behaves like a name. And we know that before using any name(like variable name) in C++, we must have a declaration for that name. So where is the declaration for this? Does the compiler implicitly declares the this in case of ctor? I mean, in case of non-static member function, i can understand that they have the declaration for this as the implicit this parameter but what happens in ctors? How inside ctors we're able to use the name this without having a declaration for that?
From the perspective of the C++ standard
The standard only describes the semantics of the this keyword and not how its value gets there. That's completely abstracted away and an implementation has great flexibility in making it happen.
From the perspective of theoretical computer science
The address of the object under construction, available via the this keyword, absolutely is an input to the initialization procedure (called "constructor" in C++) for the object. So are addresses of virtual base subobjects, which C++ also makes available via the this keyword but cannot be calculated from the main input, so any such must be additional inputs.
Note that CS tends to use "parameter" with procedures more in the sense of template parameters than dynamic variable inputs. And CS uses "function" to mean a procedure without side effects. A C++ constructor is not a CS "function" and while templated constructors are possible (parametric procedures) the value of this is an ordinary input not a parameterization.
Neither is a C++ constructor a method -- there is no polymorphic target-type-dependent dispatch, and so in particular the this input is not used for dispatch.
From the perspective of ABI rules for parameter-passing
A C++ constructor is a special member function. There's no way to construct a function pointer for it or invoke it C-style; ABI requirements for C functions do not apply.
If the platform ABI explicitly describes C++ behaviors, then there will be one or more rules for passing the value(s) of this to a C++ constructor. Those rules may or may not specify a mechanism equivalent to other function arguments, but every ABI-compliant C++ compiler targeting that platform will pass this values as required by the special rules for constructors.
Notably, the ABI for passing this to a constructor isn't required to be equivalent to how this is passed to other (non-special and non-static) member functions, although practical ABIs may well use the same approach for both.
There's no such thing as "implicit this parameter" in the standard. The standard calls it an "implicit object parameter".
An implicit object parameter is only relevant to overload resolution, it doesn't "become" this. Separately, this is defined to have the same cv qualification as the member function.
Do ctors have an implicit this parameter? This first question can also be phrased as "Do ctors also have an implicit object parameter?".
No, from [over.match.funcs]
For the purposes of overload resolution, both static and non-static member functions have an implicit object parameter, but constructors do not.
But where does that this come from.
The object being constructed.
Is the concept of implicit this parameter an implementation detail or does the standard say that all non-static member functions have an implicit this parameter, in which case, ctors are also allowed by implementations to have an implicit this parameter.
The implicit object parameter is part of the rules of overload resolution, it doesn't influence this.
How inside ctors we're able to use the name this without having a declaration for that?
this is not a name, it is a language keyword. The language defines this in a non-static member function to be a prvalue expression. Unlike an unqualified-id (that names an object) in the same position, which are glvalue expressions.
That's the language-lawyer answer. Having said that, I do find that "implicit object parameter becomes this" is a useful mental model.
Recall that constructors (and destructors) can't be cv qualified, so there isn't anything for it to distinguish in a constructor, so it doesn't matter if it exists or not.
"this" is a keyword; it doesn't need declaring, but is always available in non-static member functions.
See e.g. the draft standard here.
The keyword this names a pointer to the object for which an implicit object member function is invoked or a non-static data member's initializer is evaluated.
Note that the mechanism behind this is unspecified.

Is there a reason to differentiate between static and non-static data members in terms of prohibition of the usage of abstract types?

In class.abstract we can see in the Note 3 that:
An abstract class can be used only as a base class of some other class; no objects of an abstract class can be created except as subobjects of a class derived from it ([basic.def], [class.mem]).
This rules out the usage of abstract classes as subobjects and it makes sense to me (though this is just a note, which is non-normative, IIRC).
However, in class.mem we can read that:
The type of a non-static data member shall not be an incomplete type ([basic.types]), an abstract class type ([class.abstract]), or a (possibly multi-dimensional) array thereof.
[Note 5: In particular, a class C cannot contain a non-static member of class C, but it can contain a pointer or reference to an object of class C. — end note]
(emphasis mine)
What seems strange to me is the specific wording: "non-static". Why is it explicitly stated that this refers to non-static members? I don't believe we're allowed to have static declarations or definitions of objects of abstract types. Does the standard actually allow static data members to be abstract and no sane compiler implements that? Or it does prohibit such uses (in that case why the distinction in static vs non-static data in the aforementioned paragraph)?
Whether or not a class is abstract is not known until the class is defined.
It has always been allowed to declare a static data member with an incomplete class type, as long as the type is complete by the time the static data member is defined. Since the type of a static data member may be incomplete on its declaration, it also follows that it might be an abstract class that the compiler doesn't yet know is abstract. For this reason, it is appropriate to defer checking of the abstractness until the static data member's definition. At the time of definition, if the type of the static data member is found to be an abstract class, then the compiler should issue a diagnostic. This was the reasoning in P0929, which added the current wording in C++20.
With non-static data members, their types are required to be complete at the time of declaration, and the declaration of the non-static data member serves as a definition. So the abstractness must be checked at that point.
Why is it explicitly stated that this refers to non-static members?
Because that allows declaration of static members with incomplete type. Here is a minimal example that is allowed, but wouldn't be allowed if "non-static" wasn't explicitly stated in that rule:
struct S {
// array of unknown bound is an incomplete type
static int arr[];
//int arr2[]; // is not allowed because of the rule
};
int S::arr[3]; // definition of the static member
Does the standard actually allow static data members to be abstract
No, that would contradict the rule quoted by Vlad unless there was a more specific rule overriding it (and there isn't to my knowledge). Specifying the rule to non-static seems to be relevant to incomplete types only; and not to abstract types. As such, listing "abstract type" in the rule explicitly seems redundant and thus confusing (but not contradictory) to me Edit: Brian's answer clarifies why it makes sense.
In the first quote there is an answer to your question
An abstract class can be used only as a base class of some other
class; no objects of an abstract class can be created except as
subobjects of a class derived from it ([basic.def], [class.mem]).
So as a static data member is a separate object relative to object of the class type where it is declared it can not be created as an object of an abstract class. On the other hand, when we are speaking about creating an object of a class type we mean its non-static data members that are sub-objects of the object of the class type. Static data members are instantiated independently of the instantiation of objects of the class where they are declared.

Static Constant Class Members

Consider the following snippet:
struct Foo
{
static const T value = 123; //Where T is some POD-type
};
const T Foo::value; //Is this required?
In this case, does the standard require us to explicitly declare value in a translation unit? It seems I have conflicting information; boost and things like numeric_limits from the STL seem to do this sort of thing just like in my snippet.
OTOH, I remember reading somewhere (albeit a long long time ago) that you're still required to provide a declaration in a translation unit.
If this is the case, what about template specialization? Will each specialization require a declaration?
I'd appreciate your comments as to what the "right way" is.
You have to provide a definition in a translation unit too, in case you use the value variable. That means, if for example you read its value.
The important thing is that the compiler is not required to give a warning or error if you violate that rule. The Standard says "no diagnostic required" for a violation.
In the next C++ Standard version, the rule changed. A variable is not used when it is used as a constant expression. Simply reading value above where the variable is initialized directly in the class means that still no definition is required then.
See the definition of use in section 3.2 One Definition Rule of the Standard and requirement for a definition for static data-members in 9.4.2, paragraph 4 and 5 (in the C++98 Standard. Appears in paragraph 3 and 4 in the n2800 draft of the next Standard).
Correction: The rule already changed for c++03: If the variable appears where a integral constant expression is required, no definition is needed (quoting from an unofficial revisions list for the 2003 update), see resolution for this language defect report:
An expression is potentially evaluated unless it appears where an integral constant expression is required (see 5.19), is the operand of the sizeof operator (5.3.3), or is the operand of the typeid operator and the expression does not designate an lvalue of polymorphic class type (5.2.8)...
Note that even then, many uses are in cases where an integral constant is not required. Cases where one is, is in array dimensions or in template metaprogramming. So strictly speaking (see this report), only the c++1x solution provides really guarantee that in obvious cases also like "s == string::npos" where an integral constant is not required the definition of the static member is not needed, because the next Standard has a different, better wording of 3.2. This is however quite theoretical stuff, since most (all?) compiler don't moan anyway. Thanks for the guy in the comment section for telling me.
To add on to what litb said, from my copy of n2798:
9.4.2
[...]
2 The declaration of a static data member in its class definition is not a definition and
may be of an incomplete type other than cv-qualified void. The definition for a static
data member shall appear in a namespace scope enclosing the member’s class definition. In
the definition at namespace scope, the name of the static data member shall be qualified
by its class name using the :: operator.
You don't have to provide a definition for static integral constant members if you don't use them in some way that requires them to be stored in memory somewhere (e.g. take the address of such a member). See Stroustrup's The C++ Programming Language, section 10.4.6.2.
Edit:
Oops, I just re-read the question, and the question was for some type T. In general you would need to provide a definition, I agree. But if you used something from the int family, you wouldn't necessarily have to (with the caveat above).