Does a constructor have a "type" since it is a special member function?
Asked Answered
R

2

7

I recently learnt that constructors do not have names. I am also aware that a function has a type called a function type. For example,

void func(int)
{
}

In the above snippet, func has the function type void (int).

Now, since constructors are special member functions, 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 have a function type just like ordinary functions or ordinary member functions? If yes, then how can we find that type? Is it permitted to use decltype on constructors to find their type, like one would do for ordinary functions?

Rime answered 3/4, 2022 at 9:59 Comment(2)
you don't call constructors (except in placement new situations), don't declare them outside the context of an object and can't take its address so there's no need to get its typeFinality
Constructors do have a type in the c++26 working draft through cwg2476 as explained in my answer.Rime
R
2

Does a constructor have a "type"

This is CWG2476 whose status is DRWP(meaning accepted in C++26 working draft) which also makes constructors have a type. Note as published c++23 did not allow constructors to have a type:

C++23

Here T in T D is not allowed to be empty meaning a constructor doesn't have a type in C++23 as published.

From dcl.fct:

  1. In a declaration T D where D has the form
 D1 ( parameter-declaration-clause ) cv-qualifier-seqopt
   ref-qualifieropt noexcept-specifieropt attribute-specifier-seqopt
  1. In a declaration T D where D has the form
 D1 ( parameter-declaration-clause ) cv-qualifier-seqopt
   ref-qualifieropt noexcept-specifieropt attribute-specifier-seqopt trailing-return-type  

The optional attribute-specifier-seq appertains to the function type.

  1. A type of either form is a function type.

Note here T was not allowed to be empty before CWG2476 meaning a constructor function doesn't have a type in C++23.


CWG2476

In the working draft of C++26, T can be empty, meaning now constructors do have a type.

[Accepted as a DR at the March, 2024 meeting.]

  1. Change in 9.3.4.6 [dcl.fct] paragraph 1 as follows:

In a declaration T D where T may be empty and D has the form :

D1 ( parameter-declaration-clause ) cv-qualifier-seqopt
 ref-qualifieropt noexcept-specifieropt attribute-specifier-seqopt trailing-return-typeopt

A type of either form is a function type.

Note the emphasis on T may be empty added in the above CWG which means the constructor Name::Name(int) does have a type.

Rime answered 1/8, 2024 at 0:41 Comment(13)
And what would the type be? As far as I can tell the issue resolution neglects that case when it reaches "Otherwise, U is T.".Interlineate
@Interlineate The type is just (int) in this example. Note that if constructor don't have type then [dcl.fct.def.default2.5] through [dcl.fct.def.default2.7] could not apply because before entering thorse last three bullet it says there: "If the type of F1 differs from the type of F2 in a way...". As you see, the term used here is type of F1 which requires ctors to have a type. See this issue discussion where this is discussed in more detail.Rime
@Interlineate T can be empty so it follows that when T is empty, U can be empty too.Rime
@anatolyg 2476 is the one here. Typo at one place updated.Rime
@anatolyg Yeah, sure. I for some reason missed to correct that one. ThanksRime
Function types can't exist without the return type. I highly doubt this was the intent, it looks like the new "T may be empty" wording only tries to handle conversion operators.Casualty
@Casualty Even the C++ committee member agrees with the fact that function type can exist without return type. In the discussion it is clearly mentioned that ctor can have type (const C&&) and the committee member agrees with the points discussed there, specifically that ctor has a type. Note also that as mentioned there it was always intended for ctor to have a type otherwise [dcl.fct.def.default] can't possibly work for ctors.You can post a comment there but as per my reading ofalready postedcomments there,ctor has type without return typeGorey
@Gorey Yeah, I've posted a comment there too. I'm not entirely convinced. If it's indeed the intent, I think it at least deserves a note in the standard.Casualty
@Casualty But that is exactly what is done there. T can now be empty. This is as explicit as it gets. Ofcourse we can always add more clarification for anything, but saying T can be empty suffices. I mean you can add a comment there expalaining why exactly this doesn't suffice. Ok so it seems you've posted a comment there now.Gorey
@Gorey ''This is as explicit as it gets." Mmm... T can be empty for conversion operators and for constructors. The standard then explains how to infer the return type for conversion operators, and says absolutely nothing about the constructor, only that "the return type is T". I think most people, when realizing this, would consider this a defect. ¯\_(ツ)_/¯Casualty
@Rime I understand the reasoning in the linked discussion. I had noticed the inconsistencies that would be present if constructors were not being considered by [dcl.fct] as well, but had previously assumed that some special cases for them were simply missing. As pointed out in the discussion, the intent would have always been for constructors to have a type by the same rules. The addition "where T may be empty" would only be explanatory. So at best, CWG 2476 clarifies that constructors have type. But they would have a type in previous editions as well (even if it isn't as obvious).Interlineate
If T is empty, as in Name(int), then we reach eel.is/c++draft/dcl.fct#1.2 with D1 = Name, and compute something based on "the declaration T D1" i.e. Name, which is not a declaration. I read this as the derived-declarator-type-list does not exist. eel.is/c++draft/dcl.fct#1.5 makes U empty. So eel.is/c++draft/dcl.fct#1.sentence-3 makes the function type "[does-not-exist] function of (int) returning [empty]." Either this is very novel, or else it's so obviously impossible that [dct.fct] really (still) means "The function type does not exist."Seven
@Seven Do also check out the new reported cwg issue.Rime
D
6

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)

Dollfuss answered 3/4, 2022 at 10:21 Comment(5)
In the last statement that you quoted, why isn't return type mentioned there? Is the return type included in trailing part? I mean doesn't the signature of an ordinary function include the return type also?Rime
@Mike #290538 Free and member functions cannot be differentiated based on return type. Only (member) function template specializations can be.Interlineate
A tiny bone to pick: you can't examine the types of member functions too, but they're specified.Eskimoaleut
@PasserBy - You can derive other examinable types from them, though. A pointer to member function will carry the entire abominable function type of a member function. And of course, the entire process of forming a pointer to member requires naming the function. So I don't feel like I'm misdirecting too much here.Dollfuss
@Interlineate Through CWG2479 as DRWP, constructors do have a type as explained in my answerRime
R
2

Does a constructor have a "type"

This is CWG2476 whose status is DRWP(meaning accepted in C++26 working draft) which also makes constructors have a type. Note as published c++23 did not allow constructors to have a type:

C++23

Here T in T D is not allowed to be empty meaning a constructor doesn't have a type in C++23 as published.

From dcl.fct:

  1. In a declaration T D where D has the form
 D1 ( parameter-declaration-clause ) cv-qualifier-seqopt
   ref-qualifieropt noexcept-specifieropt attribute-specifier-seqopt
  1. In a declaration T D where D has the form
 D1 ( parameter-declaration-clause ) cv-qualifier-seqopt
   ref-qualifieropt noexcept-specifieropt attribute-specifier-seqopt trailing-return-type  

The optional attribute-specifier-seq appertains to the function type.

  1. A type of either form is a function type.

Note here T was not allowed to be empty before CWG2476 meaning a constructor function doesn't have a type in C++23.


CWG2476

In the working draft of C++26, T can be empty, meaning now constructors do have a type.

[Accepted as a DR at the March, 2024 meeting.]

  1. Change in 9.3.4.6 [dcl.fct] paragraph 1 as follows:

In a declaration T D where T may be empty and D has the form :

D1 ( parameter-declaration-clause ) cv-qualifier-seqopt
 ref-qualifieropt noexcept-specifieropt attribute-specifier-seqopt trailing-return-typeopt

A type of either form is a function type.

Note the emphasis on T may be empty added in the above CWG which means the constructor Name::Name(int) does have a type.

Rime answered 1/8, 2024 at 0:41 Comment(13)
And what would the type be? As far as I can tell the issue resolution neglects that case when it reaches "Otherwise, U is T.".Interlineate
@Interlineate The type is just (int) in this example. Note that if constructor don't have type then [dcl.fct.def.default2.5] through [dcl.fct.def.default2.7] could not apply because before entering thorse last three bullet it says there: "If the type of F1 differs from the type of F2 in a way...". As you see, the term used here is type of F1 which requires ctors to have a type. See this issue discussion where this is discussed in more detail.Rime
@Interlineate T can be empty so it follows that when T is empty, U can be empty too.Rime
@anatolyg 2476 is the one here. Typo at one place updated.Rime
@anatolyg Yeah, sure. I for some reason missed to correct that one. ThanksRime
Function types can't exist without the return type. I highly doubt this was the intent, it looks like the new "T may be empty" wording only tries to handle conversion operators.Casualty
@Casualty Even the C++ committee member agrees with the fact that function type can exist without return type. In the discussion it is clearly mentioned that ctor can have type (const C&&) and the committee member agrees with the points discussed there, specifically that ctor has a type. Note also that as mentioned there it was always intended for ctor to have a type otherwise [dcl.fct.def.default] can't possibly work for ctors.You can post a comment there but as per my reading ofalready postedcomments there,ctor has type without return typeGorey
@Gorey Yeah, I've posted a comment there too. I'm not entirely convinced. If it's indeed the intent, I think it at least deserves a note in the standard.Casualty
@Casualty But that is exactly what is done there. T can now be empty. This is as explicit as it gets. Ofcourse we can always add more clarification for anything, but saying T can be empty suffices. I mean you can add a comment there expalaining why exactly this doesn't suffice. Ok so it seems you've posted a comment there now.Gorey
@Gorey ''This is as explicit as it gets." Mmm... T can be empty for conversion operators and for constructors. The standard then explains how to infer the return type for conversion operators, and says absolutely nothing about the constructor, only that "the return type is T". I think most people, when realizing this, would consider this a defect. ¯\_(ツ)_/¯Casualty
@Rime I understand the reasoning in the linked discussion. I had noticed the inconsistencies that would be present if constructors were not being considered by [dcl.fct] as well, but had previously assumed that some special cases for them were simply missing. As pointed out in the discussion, the intent would have always been for constructors to have a type by the same rules. The addition "where T may be empty" would only be explanatory. So at best, CWG 2476 clarifies that constructors have type. But they would have a type in previous editions as well (even if it isn't as obvious).Interlineate
If T is empty, as in Name(int), then we reach eel.is/c++draft/dcl.fct#1.2 with D1 = Name, and compute something based on "the declaration T D1" i.e. Name, which is not a declaration. I read this as the derived-declarator-type-list does not exist. eel.is/c++draft/dcl.fct#1.5 makes U empty. So eel.is/c++draft/dcl.fct#1.sentence-3 makes the function type "[does-not-exist] function of (int) returning [empty]." Either this is very novel, or else it's so obviously impossible that [dct.fct] really (still) means "The function type does not exist."Seven
@Seven Do also check out the new reported cwg issue.Rime

© 2022 - 2025 — McMap. All rights reserved.