Will a class that is declared within a member initializer of a constructor of another class be visible outside it?

The name of the pictureThe name of the pictureThe name of the pictureClash Royale CLAN TAG#URR8PPP












17















Consider the following piece of code:



struct Foo 
void* p;
Foo() : p(class Bar*)0
;

Bar* bar;


The latest versions of GCC (8.2) and Clang (7.0.0) fail to compile it. So does ICC (19.0.1).

However MSVC (v19.16) compiles it cleanly.



The error from GCC is: error: 'Bar' does not name a type; did you mean 'char'?

Clang and ICC issue similar messages.



Conformance viewer for all four compilers at godbolt.



So which of the compiler(s) is correct as per the standard?










share|improve this question



















  • 6





    did you mean 'char'?: gcc sound like early '00s google :D

    – YSC
    Feb 12 at 12:24







  • 1





    I believe that MSVC is correct, and Richard Smith seems to agree. (From zygoloid.github.io/cppcontest2018.html, section The Most Surprising: 'GCC, Clang, and ICC all get this "wrong", but MSVC follows the spec.')

    – cpplearner
    Feb 12 at 13:11







  • 1





    @P.W My understanding is largely the same as eerorika's: 1. The identifier is "declared in the smallest namespace or block scope that contains the declaration." 2. Since block scope is the production of compound statement ([stmt.block]), and the mem-initializer-list appears outside any compound statement, the "smallest namespace or block scope" can only be the global namespace scope.

    – cpplearner
    Feb 12 at 13:27






  • 1





    I really want to know why this was close-voted for "unclear problem statement"...

    – Moira
    Feb 12 at 14:15







  • 2





    @T.E.D. then you've come to the wrong language ;)

    – Quentin
    Feb 12 at 14:57















17















Consider the following piece of code:



struct Foo 
void* p;
Foo() : p(class Bar*)0
;

Bar* bar;


The latest versions of GCC (8.2) and Clang (7.0.0) fail to compile it. So does ICC (19.0.1).

However MSVC (v19.16) compiles it cleanly.



The error from GCC is: error: 'Bar' does not name a type; did you mean 'char'?

Clang and ICC issue similar messages.



Conformance viewer for all four compilers at godbolt.



So which of the compiler(s) is correct as per the standard?










share|improve this question



















  • 6





    did you mean 'char'?: gcc sound like early '00s google :D

    – YSC
    Feb 12 at 12:24







  • 1





    I believe that MSVC is correct, and Richard Smith seems to agree. (From zygoloid.github.io/cppcontest2018.html, section The Most Surprising: 'GCC, Clang, and ICC all get this "wrong", but MSVC follows the spec.')

    – cpplearner
    Feb 12 at 13:11







  • 1





    @P.W My understanding is largely the same as eerorika's: 1. The identifier is "declared in the smallest namespace or block scope that contains the declaration." 2. Since block scope is the production of compound statement ([stmt.block]), and the mem-initializer-list appears outside any compound statement, the "smallest namespace or block scope" can only be the global namespace scope.

    – cpplearner
    Feb 12 at 13:27






  • 1





    I really want to know why this was close-voted for "unclear problem statement"...

    – Moira
    Feb 12 at 14:15







  • 2





    @T.E.D. then you've come to the wrong language ;)

    – Quentin
    Feb 12 at 14:57













17












17








17








Consider the following piece of code:



struct Foo 
void* p;
Foo() : p(class Bar*)0
;

Bar* bar;


The latest versions of GCC (8.2) and Clang (7.0.0) fail to compile it. So does ICC (19.0.1).

However MSVC (v19.16) compiles it cleanly.



The error from GCC is: error: 'Bar' does not name a type; did you mean 'char'?

Clang and ICC issue similar messages.



Conformance viewer for all four compilers at godbolt.



So which of the compiler(s) is correct as per the standard?










share|improve this question
















Consider the following piece of code:



struct Foo 
void* p;
Foo() : p(class Bar*)0
;

Bar* bar;


The latest versions of GCC (8.2) and Clang (7.0.0) fail to compile it. So does ICC (19.0.1).

However MSVC (v19.16) compiles it cleanly.



The error from GCC is: error: 'Bar' does not name a type; did you mean 'char'?

Clang and ICC issue similar messages.



Conformance viewer for all four compilers at godbolt.



So which of the compiler(s) is correct as per the standard?







c++ language-lawyer






share|improve this question















share|improve this question













share|improve this question




share|improve this question








edited Feb 12 at 12:04







P.W

















asked Feb 12 at 11:59









P.WP.W

16.1k31455




16.1k31455







  • 6





    did you mean 'char'?: gcc sound like early '00s google :D

    – YSC
    Feb 12 at 12:24







  • 1





    I believe that MSVC is correct, and Richard Smith seems to agree. (From zygoloid.github.io/cppcontest2018.html, section The Most Surprising: 'GCC, Clang, and ICC all get this "wrong", but MSVC follows the spec.')

    – cpplearner
    Feb 12 at 13:11







  • 1





    @P.W My understanding is largely the same as eerorika's: 1. The identifier is "declared in the smallest namespace or block scope that contains the declaration." 2. Since block scope is the production of compound statement ([stmt.block]), and the mem-initializer-list appears outside any compound statement, the "smallest namespace or block scope" can only be the global namespace scope.

    – cpplearner
    Feb 12 at 13:27






  • 1





    I really want to know why this was close-voted for "unclear problem statement"...

    – Moira
    Feb 12 at 14:15







  • 2





    @T.E.D. then you've come to the wrong language ;)

    – Quentin
    Feb 12 at 14:57












  • 6





    did you mean 'char'?: gcc sound like early '00s google :D

    – YSC
    Feb 12 at 12:24







  • 1





    I believe that MSVC is correct, and Richard Smith seems to agree. (From zygoloid.github.io/cppcontest2018.html, section The Most Surprising: 'GCC, Clang, and ICC all get this "wrong", but MSVC follows the spec.')

    – cpplearner
    Feb 12 at 13:11







  • 1





    @P.W My understanding is largely the same as eerorika's: 1. The identifier is "declared in the smallest namespace or block scope that contains the declaration." 2. Since block scope is the production of compound statement ([stmt.block]), and the mem-initializer-list appears outside any compound statement, the "smallest namespace or block scope" can only be the global namespace scope.

    – cpplearner
    Feb 12 at 13:27






  • 1





    I really want to know why this was close-voted for "unclear problem statement"...

    – Moira
    Feb 12 at 14:15







  • 2





    @T.E.D. then you've come to the wrong language ;)

    – Quentin
    Feb 12 at 14:57







6




6





did you mean 'char'?: gcc sound like early '00s google :D

– YSC
Feb 12 at 12:24






did you mean 'char'?: gcc sound like early '00s google :D

– YSC
Feb 12 at 12:24





1




1





I believe that MSVC is correct, and Richard Smith seems to agree. (From zygoloid.github.io/cppcontest2018.html, section The Most Surprising: 'GCC, Clang, and ICC all get this "wrong", but MSVC follows the spec.')

– cpplearner
Feb 12 at 13:11






I believe that MSVC is correct, and Richard Smith seems to agree. (From zygoloid.github.io/cppcontest2018.html, section The Most Surprising: 'GCC, Clang, and ICC all get this "wrong", but MSVC follows the spec.')

– cpplearner
Feb 12 at 13:11





1




1





@P.W My understanding is largely the same as eerorika's: 1. The identifier is "declared in the smallest namespace or block scope that contains the declaration." 2. Since block scope is the production of compound statement ([stmt.block]), and the mem-initializer-list appears outside any compound statement, the "smallest namespace or block scope" can only be the global namespace scope.

– cpplearner
Feb 12 at 13:27





@P.W My understanding is largely the same as eerorika's: 1. The identifier is "declared in the smallest namespace or block scope that contains the declaration." 2. Since block scope is the production of compound statement ([stmt.block]), and the mem-initializer-list appears outside any compound statement, the "smallest namespace or block scope" can only be the global namespace scope.

– cpplearner
Feb 12 at 13:27




1




1





I really want to know why this was close-voted for "unclear problem statement"...

– Moira
Feb 12 at 14:15






I really want to know why this was close-voted for "unclear problem statement"...

– Moira
Feb 12 at 14:15





2




2





@T.E.D. then you've come to the wrong language ;)

– Quentin
Feb 12 at 14:57





@T.E.D. then you've come to the wrong language ;)

– Quentin
Feb 12 at 14:57












2 Answers
2






active

oldest

votes


















13















  • [basic.lookup.elab] ... If the elaborated-type-specifier is introduced by
    the class-key and this lookup does not find a previously declared type-name ... the elaborated-type-specifier is a declaration that introduces the class-name as described in [basic.scope.pdecl]



  • [basic.scope.pdecl] - for an elaborated-type-specifier of the form



    class-key identifier



    if the elaborated-type-specifier is used in the decl-specifier-seq or parameter-declaration-clause of a
    function defined in namespace scope [does not apply because of scope], ... otherwise, except as a friend declaration, the identifier is declared in the
    smallest namespace or block scope that contains the declaration.





Now, the tricky bit. Is the member initialiser list "contained" in the scope of the constructor or not? If not, then the smallest block or namespace scope is the global namespace and the program would be well-formed. If yes, then the smallest scope is the block scope of the constructor, and thus the class would not be declared in the global scope.



As far as I can tell, there is no rule saying that mem-init-list is "contained in the block scope of the constructor". It is outside the curly brackets that delimit the scope. As such, the program is well-formed.



Mem-init-list is part of the constructor body [dcl.fct.def.general], but that body is neither a block scope nor a namespace scope, so it is not relevant to the rule in [basic.scope.pdecl].






share|improve this answer




















  • 3





    I don't get how "name" and "evaluate" can mix: names are looked up and expression are evaluated, right?

    – YSC
    Feb 12 at 12:37











  • @YSC Evaluations and declarations do seem orthogonal to me, but I include that rule because it's the only rule (besides one about where names are looked up from) about scope in the member init list that I could find. I could be wrong if I were to assume that the rule does not apply.

    – eerorika
    Feb 12 at 12:41












  • I don't think we could find a better matching rule, though I'm trying. It may be a wording issue.

    – YSC
    Feb 12 at 12:43











  • BTW, I think you lack an answer to the question itself: is it legal?

    – YSC
    Feb 12 at 12:44











  • @eerorika: The last para of your answer sums up my predicament too.

    – P.W
    Feb 12 at 12:44


















8














This is about declaration rules and scope rules; (elaborated type specifier may also declare a class-name in an expression).




scope.declarative/3:
The names declared by a declaration are introduced into the scope in
which the declaration occurs, except that the presence of a friend
specifier, certain uses of the elaborated-type-specifier
([dcl.type.elab]), and using-directives ([namespace.udir]) alter this
general behavior.




a constructor has a scope (as eeroika's answer mentions), likewise anything declared therein. That is why this should be valid



struct Foo 
void* p;
Foo() : p(class Bar*)0
Bar* bar;

;


https://godbolt.org/z/m3Tdle



But not:



struct Foo 
void* p;
Foo() : p(class Bar*)0
//Scope of Bar only exists here

;

Bar* bar;


EDIT:
More details about this:



The so-called "forward declaration" in C++ is technically one of multiple "forms" of elaborated-type-specifier; And only two forms may introduce a name. (Emphasis mine)




The point of declaration of a class first declared in an
elaborated-type-specifier is as follows:




  • (7.1) for a declaration of the form



    class-key attribute-specifier-seqopt identifier; //<-- Note the semi colon



    the identifier is declared to be a class-name in the scope that contains
    the declaration, otherwise




  • (7.2) for an elaborated-type-specifier of the form



    class-key identifier



    if the elaborated-type-specifier is used in the decl-specifier-seq or parameter-declaration-clause of a function defined in namespace scope, the identifier is declared as a class-name in the namespace that contains the declaration; otherwise, except as a friend declaration, the identifier is declared in the smallest namespace or block scope that contains the declaration. [ Note: These rules also apply within templates. —- end note ] [ Note:
    Other forms of elaborated-type-specifier do not declare a new name,
    and therefore must refer to an existing type-name.
    See
    [basic.lookup.elab] and [dcl.type.elab]. —- end note ]






Here's another interesting bit about it;




dcl.type.elab/1 An
attribute-specifier-seq shall not appear in an elaborated-type-specifier unless the latter is the sole constituent of
a declaration...




That is why this (below) is valid, See it here https://godbolt.org/z/IkmvGn;



void foo()
void* n = (class Boo*)(0);
Boo* b;


class [[deprecated("WTF")]] Mew;


But this, (below) is incorrect1; See it here https://godbolt.org/z/8X1QKq;



void foo()
void* n = (class [[deprecated("WTF")]] Boo*)(0);


class [[deprecated("WTF")]] Mew;



  • 1Strangely, GCC accepts it, but gives this warning:



    attributes ignored on elaborated-type-specifier that is not a forward declaration [-Wattributes]



To see all the other "forms" of elaborated-type-specifier see dcl.type.elab




Edit 2



To double-check my understanding, I asked further, and @Simon Brand made me realise some edge cases, and that is partly hinted by basic.scope/declarative-4.note-2. But chiefly by the second quote in this answer basic.scope/pdecl-7.2



An elaborated-type-specifier within a function-parameter-scope will leak its class-name to the enclosing namespace (note: this is not class-scope).



This is valid https://godbolt.org/z/Fx5B83:



struct Foo 
static void foo(void* = (class Bat*)0); //Leaks it past class-scope
;

void moo()
Bat* m;



Even if you hide it in nested classes https://godbolt.org/z/40Raup:



struct Foo 
class Moo
class Mew
void foo(void* = (class Bat*)0); //
;
;
;

void moo()
Bat* m;



... However, the name stops leaking at the closest namespace https://godbolt.org/z/YDljDo .



namespace zoo 
struct Foo
class Moo
class Mew
void foo(void* = (class Bat*)0);
;
;
;

void moo()
zoo::Bat* m;



Finally,



mem-init-list is a compound-statement and does not have a function-parameter-scope; So, if you want to achieve, the leakage, do the declaration in the function-parameter-scope. See https://godbolt.org/z/CqejYS



struct Foo 
void* p;
Foo(void* = (class Zoo*)(0))
: p(class Bar*)0
Bar* bar;

;

Zoo* m; //Zoo* leaked
//Bar* n //Bar* did not leak





share|improve this answer

























  • But saying class Bar should implicitly forward declare it, no? Even there?

    – Lightness Races in Orbit
    Feb 13 at 10:42







  • 1





    @LightnessRacesinOrbit, If I am not mistaken, the so-called "forward declaration" is actually one of multiple forms of elaborated-type-specifier; And only two forms introduce a name. I'm not sure I understand your comment correctly though....

    – WhiZTiM
    Feb 13 at 11:34












  • Nope, you understood correctly, and also (it seems) answered correctly - thanks :)

    – Lightness Races in Orbit
    Feb 13 at 12:01











  • (Would you consider adding that to the answer? I think it's the key)

    – Lightness Races in Orbit
    Feb 13 at 12:01











  • @LightnessRacesinOrbit, Thanks for the suggestion, done... improved the answer a bit :-)

    – WhiZTiM
    Feb 13 at 18:16










Your Answer






StackExchange.ifUsing("editor", function ()
StackExchange.using("externalEditor", function ()
StackExchange.using("snippets", function ()
StackExchange.snippets.init();
);
);
, "code-snippets");

StackExchange.ready(function()
var channelOptions =
tags: "".split(" "),
id: "1"
;
initTagRenderer("".split(" "), "".split(" "), channelOptions);

StackExchange.using("externalEditor", function()
// Have to fire editor after snippets, if snippets enabled
if (StackExchange.settings.snippets.snippetsEnabled)
StackExchange.using("snippets", function()
createEditor();
);

else
createEditor();

);

function createEditor()
StackExchange.prepareEditor(
heartbeatType: 'answer',
autoActivateHeartbeat: false,
convertImagesToLinks: true,
noModals: true,
showLowRepImageUploadWarning: true,
reputationToPostImages: 10,
bindNavPrevention: true,
postfix: "",
imageUploader:
brandingHtml: "Powered by u003ca class="icon-imgur-white" href="https://imgur.com/"u003eu003c/au003e",
contentPolicyHtml: "User contributions licensed under u003ca href="https://creativecommons.org/licenses/by-sa/3.0/"u003ecc by-sa 3.0 with attribution requiredu003c/au003e u003ca href="https://stackoverflow.com/legal/content-policy"u003e(content policy)u003c/au003e",
allowUrls: true
,
onDemand: true,
discardSelector: ".discard-answer"
,immediatelyShowMarkdownHelp:true
);



);













draft saved

draft discarded


















StackExchange.ready(
function ()
StackExchange.openid.initPostLogin('.new-post-login', 'https%3a%2f%2fstackoverflow.com%2fquestions%2f54649661%2fwill-a-class-that-is-declared-within-a-member-initializer-of-a-constructor-of-an%23new-answer', 'question_page');

);

Post as a guest















Required, but never shown

























2 Answers
2






active

oldest

votes








2 Answers
2






active

oldest

votes









active

oldest

votes






active

oldest

votes









13















  • [basic.lookup.elab] ... If the elaborated-type-specifier is introduced by
    the class-key and this lookup does not find a previously declared type-name ... the elaborated-type-specifier is a declaration that introduces the class-name as described in [basic.scope.pdecl]



  • [basic.scope.pdecl] - for an elaborated-type-specifier of the form



    class-key identifier



    if the elaborated-type-specifier is used in the decl-specifier-seq or parameter-declaration-clause of a
    function defined in namespace scope [does not apply because of scope], ... otherwise, except as a friend declaration, the identifier is declared in the
    smallest namespace or block scope that contains the declaration.





Now, the tricky bit. Is the member initialiser list "contained" in the scope of the constructor or not? If not, then the smallest block or namespace scope is the global namespace and the program would be well-formed. If yes, then the smallest scope is the block scope of the constructor, and thus the class would not be declared in the global scope.



As far as I can tell, there is no rule saying that mem-init-list is "contained in the block scope of the constructor". It is outside the curly brackets that delimit the scope. As such, the program is well-formed.



Mem-init-list is part of the constructor body [dcl.fct.def.general], but that body is neither a block scope nor a namespace scope, so it is not relevant to the rule in [basic.scope.pdecl].






share|improve this answer




















  • 3





    I don't get how "name" and "evaluate" can mix: names are looked up and expression are evaluated, right?

    – YSC
    Feb 12 at 12:37











  • @YSC Evaluations and declarations do seem orthogonal to me, but I include that rule because it's the only rule (besides one about where names are looked up from) about scope in the member init list that I could find. I could be wrong if I were to assume that the rule does not apply.

    – eerorika
    Feb 12 at 12:41












  • I don't think we could find a better matching rule, though I'm trying. It may be a wording issue.

    – YSC
    Feb 12 at 12:43











  • BTW, I think you lack an answer to the question itself: is it legal?

    – YSC
    Feb 12 at 12:44











  • @eerorika: The last para of your answer sums up my predicament too.

    – P.W
    Feb 12 at 12:44















13















  • [basic.lookup.elab] ... If the elaborated-type-specifier is introduced by
    the class-key and this lookup does not find a previously declared type-name ... the elaborated-type-specifier is a declaration that introduces the class-name as described in [basic.scope.pdecl]



  • [basic.scope.pdecl] - for an elaborated-type-specifier of the form



    class-key identifier



    if the elaborated-type-specifier is used in the decl-specifier-seq or parameter-declaration-clause of a
    function defined in namespace scope [does not apply because of scope], ... otherwise, except as a friend declaration, the identifier is declared in the
    smallest namespace or block scope that contains the declaration.





Now, the tricky bit. Is the member initialiser list "contained" in the scope of the constructor or not? If not, then the smallest block or namespace scope is the global namespace and the program would be well-formed. If yes, then the smallest scope is the block scope of the constructor, and thus the class would not be declared in the global scope.



As far as I can tell, there is no rule saying that mem-init-list is "contained in the block scope of the constructor". It is outside the curly brackets that delimit the scope. As such, the program is well-formed.



Mem-init-list is part of the constructor body [dcl.fct.def.general], but that body is neither a block scope nor a namespace scope, so it is not relevant to the rule in [basic.scope.pdecl].






share|improve this answer




















  • 3





    I don't get how "name" and "evaluate" can mix: names are looked up and expression are evaluated, right?

    – YSC
    Feb 12 at 12:37











  • @YSC Evaluations and declarations do seem orthogonal to me, but I include that rule because it's the only rule (besides one about where names are looked up from) about scope in the member init list that I could find. I could be wrong if I were to assume that the rule does not apply.

    – eerorika
    Feb 12 at 12:41












  • I don't think we could find a better matching rule, though I'm trying. It may be a wording issue.

    – YSC
    Feb 12 at 12:43











  • BTW, I think you lack an answer to the question itself: is it legal?

    – YSC
    Feb 12 at 12:44











  • @eerorika: The last para of your answer sums up my predicament too.

    – P.W
    Feb 12 at 12:44













13












13








13








  • [basic.lookup.elab] ... If the elaborated-type-specifier is introduced by
    the class-key and this lookup does not find a previously declared type-name ... the elaborated-type-specifier is a declaration that introduces the class-name as described in [basic.scope.pdecl]



  • [basic.scope.pdecl] - for an elaborated-type-specifier of the form



    class-key identifier



    if the elaborated-type-specifier is used in the decl-specifier-seq or parameter-declaration-clause of a
    function defined in namespace scope [does not apply because of scope], ... otherwise, except as a friend declaration, the identifier is declared in the
    smallest namespace or block scope that contains the declaration.





Now, the tricky bit. Is the member initialiser list "contained" in the scope of the constructor or not? If not, then the smallest block or namespace scope is the global namespace and the program would be well-formed. If yes, then the smallest scope is the block scope of the constructor, and thus the class would not be declared in the global scope.



As far as I can tell, there is no rule saying that mem-init-list is "contained in the block scope of the constructor". It is outside the curly brackets that delimit the scope. As such, the program is well-formed.



Mem-init-list is part of the constructor body [dcl.fct.def.general], but that body is neither a block scope nor a namespace scope, so it is not relevant to the rule in [basic.scope.pdecl].






share|improve this answer
















  • [basic.lookup.elab] ... If the elaborated-type-specifier is introduced by
    the class-key and this lookup does not find a previously declared type-name ... the elaborated-type-specifier is a declaration that introduces the class-name as described in [basic.scope.pdecl]



  • [basic.scope.pdecl] - for an elaborated-type-specifier of the form



    class-key identifier



    if the elaborated-type-specifier is used in the decl-specifier-seq or parameter-declaration-clause of a
    function defined in namespace scope [does not apply because of scope], ... otherwise, except as a friend declaration, the identifier is declared in the
    smallest namespace or block scope that contains the declaration.





Now, the tricky bit. Is the member initialiser list "contained" in the scope of the constructor or not? If not, then the smallest block or namespace scope is the global namespace and the program would be well-formed. If yes, then the smallest scope is the block scope of the constructor, and thus the class would not be declared in the global scope.



As far as I can tell, there is no rule saying that mem-init-list is "contained in the block scope of the constructor". It is outside the curly brackets that delimit the scope. As such, the program is well-formed.



Mem-init-list is part of the constructor body [dcl.fct.def.general], but that body is neither a block scope nor a namespace scope, so it is not relevant to the rule in [basic.scope.pdecl].







share|improve this answer














share|improve this answer



share|improve this answer








edited Feb 13 at 10:58

























answered Feb 12 at 12:30









eerorikaeerorika

84.8k663132




84.8k663132







  • 3





    I don't get how "name" and "evaluate" can mix: names are looked up and expression are evaluated, right?

    – YSC
    Feb 12 at 12:37











  • @YSC Evaluations and declarations do seem orthogonal to me, but I include that rule because it's the only rule (besides one about where names are looked up from) about scope in the member init list that I could find. I could be wrong if I were to assume that the rule does not apply.

    – eerorika
    Feb 12 at 12:41












  • I don't think we could find a better matching rule, though I'm trying. It may be a wording issue.

    – YSC
    Feb 12 at 12:43











  • BTW, I think you lack an answer to the question itself: is it legal?

    – YSC
    Feb 12 at 12:44











  • @eerorika: The last para of your answer sums up my predicament too.

    – P.W
    Feb 12 at 12:44












  • 3





    I don't get how "name" and "evaluate" can mix: names are looked up and expression are evaluated, right?

    – YSC
    Feb 12 at 12:37











  • @YSC Evaluations and declarations do seem orthogonal to me, but I include that rule because it's the only rule (besides one about where names are looked up from) about scope in the member init list that I could find. I could be wrong if I were to assume that the rule does not apply.

    – eerorika
    Feb 12 at 12:41












  • I don't think we could find a better matching rule, though I'm trying. It may be a wording issue.

    – YSC
    Feb 12 at 12:43











  • BTW, I think you lack an answer to the question itself: is it legal?

    – YSC
    Feb 12 at 12:44











  • @eerorika: The last para of your answer sums up my predicament too.

    – P.W
    Feb 12 at 12:44







3




3





I don't get how "name" and "evaluate" can mix: names are looked up and expression are evaluated, right?

– YSC
Feb 12 at 12:37





I don't get how "name" and "evaluate" can mix: names are looked up and expression are evaluated, right?

– YSC
Feb 12 at 12:37













@YSC Evaluations and declarations do seem orthogonal to me, but I include that rule because it's the only rule (besides one about where names are looked up from) about scope in the member init list that I could find. I could be wrong if I were to assume that the rule does not apply.

– eerorika
Feb 12 at 12:41






@YSC Evaluations and declarations do seem orthogonal to me, but I include that rule because it's the only rule (besides one about where names are looked up from) about scope in the member init list that I could find. I could be wrong if I were to assume that the rule does not apply.

– eerorika
Feb 12 at 12:41














I don't think we could find a better matching rule, though I'm trying. It may be a wording issue.

– YSC
Feb 12 at 12:43





I don't think we could find a better matching rule, though I'm trying. It may be a wording issue.

– YSC
Feb 12 at 12:43













BTW, I think you lack an answer to the question itself: is it legal?

– YSC
Feb 12 at 12:44





BTW, I think you lack an answer to the question itself: is it legal?

– YSC
Feb 12 at 12:44













@eerorika: The last para of your answer sums up my predicament too.

– P.W
Feb 12 at 12:44





@eerorika: The last para of your answer sums up my predicament too.

– P.W
Feb 12 at 12:44













8














This is about declaration rules and scope rules; (elaborated type specifier may also declare a class-name in an expression).




scope.declarative/3:
The names declared by a declaration are introduced into the scope in
which the declaration occurs, except that the presence of a friend
specifier, certain uses of the elaborated-type-specifier
([dcl.type.elab]), and using-directives ([namespace.udir]) alter this
general behavior.




a constructor has a scope (as eeroika's answer mentions), likewise anything declared therein. That is why this should be valid



struct Foo 
void* p;
Foo() : p(class Bar*)0
Bar* bar;

;


https://godbolt.org/z/m3Tdle



But not:



struct Foo 
void* p;
Foo() : p(class Bar*)0
//Scope of Bar only exists here

;

Bar* bar;


EDIT:
More details about this:



The so-called "forward declaration" in C++ is technically one of multiple "forms" of elaborated-type-specifier; And only two forms may introduce a name. (Emphasis mine)




The point of declaration of a class first declared in an
elaborated-type-specifier is as follows:




  • (7.1) for a declaration of the form



    class-key attribute-specifier-seqopt identifier; //<-- Note the semi colon



    the identifier is declared to be a class-name in the scope that contains
    the declaration, otherwise




  • (7.2) for an elaborated-type-specifier of the form



    class-key identifier



    if the elaborated-type-specifier is used in the decl-specifier-seq or parameter-declaration-clause of a function defined in namespace scope, the identifier is declared as a class-name in the namespace that contains the declaration; otherwise, except as a friend declaration, the identifier is declared in the smallest namespace or block scope that contains the declaration. [ Note: These rules also apply within templates. —- end note ] [ Note:
    Other forms of elaborated-type-specifier do not declare a new name,
    and therefore must refer to an existing type-name.
    See
    [basic.lookup.elab] and [dcl.type.elab]. —- end note ]






Here's another interesting bit about it;




dcl.type.elab/1 An
attribute-specifier-seq shall not appear in an elaborated-type-specifier unless the latter is the sole constituent of
a declaration...




That is why this (below) is valid, See it here https://godbolt.org/z/IkmvGn;



void foo()
void* n = (class Boo*)(0);
Boo* b;


class [[deprecated("WTF")]] Mew;


But this, (below) is incorrect1; See it here https://godbolt.org/z/8X1QKq;



void foo()
void* n = (class [[deprecated("WTF")]] Boo*)(0);


class [[deprecated("WTF")]] Mew;



  • 1Strangely, GCC accepts it, but gives this warning:



    attributes ignored on elaborated-type-specifier that is not a forward declaration [-Wattributes]



To see all the other "forms" of elaborated-type-specifier see dcl.type.elab




Edit 2



To double-check my understanding, I asked further, and @Simon Brand made me realise some edge cases, and that is partly hinted by basic.scope/declarative-4.note-2. But chiefly by the second quote in this answer basic.scope/pdecl-7.2



An elaborated-type-specifier within a function-parameter-scope will leak its class-name to the enclosing namespace (note: this is not class-scope).



This is valid https://godbolt.org/z/Fx5B83:



struct Foo 
static void foo(void* = (class Bat*)0); //Leaks it past class-scope
;

void moo()
Bat* m;



Even if you hide it in nested classes https://godbolt.org/z/40Raup:



struct Foo 
class Moo
class Mew
void foo(void* = (class Bat*)0); //
;
;
;

void moo()
Bat* m;



... However, the name stops leaking at the closest namespace https://godbolt.org/z/YDljDo .



namespace zoo 
struct Foo
class Moo
class Mew
void foo(void* = (class Bat*)0);
;
;
;

void moo()
zoo::Bat* m;



Finally,



mem-init-list is a compound-statement and does not have a function-parameter-scope; So, if you want to achieve, the leakage, do the declaration in the function-parameter-scope. See https://godbolt.org/z/CqejYS



struct Foo 
void* p;
Foo(void* = (class Zoo*)(0))
: p(class Bar*)0
Bar* bar;

;

Zoo* m; //Zoo* leaked
//Bar* n //Bar* did not leak





share|improve this answer

























  • But saying class Bar should implicitly forward declare it, no? Even there?

    – Lightness Races in Orbit
    Feb 13 at 10:42







  • 1





    @LightnessRacesinOrbit, If I am not mistaken, the so-called "forward declaration" is actually one of multiple forms of elaborated-type-specifier; And only two forms introduce a name. I'm not sure I understand your comment correctly though....

    – WhiZTiM
    Feb 13 at 11:34












  • Nope, you understood correctly, and also (it seems) answered correctly - thanks :)

    – Lightness Races in Orbit
    Feb 13 at 12:01











  • (Would you consider adding that to the answer? I think it's the key)

    – Lightness Races in Orbit
    Feb 13 at 12:01











  • @LightnessRacesinOrbit, Thanks for the suggestion, done... improved the answer a bit :-)

    – WhiZTiM
    Feb 13 at 18:16















8














This is about declaration rules and scope rules; (elaborated type specifier may also declare a class-name in an expression).




scope.declarative/3:
The names declared by a declaration are introduced into the scope in
which the declaration occurs, except that the presence of a friend
specifier, certain uses of the elaborated-type-specifier
([dcl.type.elab]), and using-directives ([namespace.udir]) alter this
general behavior.




a constructor has a scope (as eeroika's answer mentions), likewise anything declared therein. That is why this should be valid



struct Foo 
void* p;
Foo() : p(class Bar*)0
Bar* bar;

;


https://godbolt.org/z/m3Tdle



But not:



struct Foo 
void* p;
Foo() : p(class Bar*)0
//Scope of Bar only exists here

;

Bar* bar;


EDIT:
More details about this:



The so-called "forward declaration" in C++ is technically one of multiple "forms" of elaborated-type-specifier; And only two forms may introduce a name. (Emphasis mine)




The point of declaration of a class first declared in an
elaborated-type-specifier is as follows:




  • (7.1) for a declaration of the form



    class-key attribute-specifier-seqopt identifier; //<-- Note the semi colon



    the identifier is declared to be a class-name in the scope that contains
    the declaration, otherwise




  • (7.2) for an elaborated-type-specifier of the form



    class-key identifier



    if the elaborated-type-specifier is used in the decl-specifier-seq or parameter-declaration-clause of a function defined in namespace scope, the identifier is declared as a class-name in the namespace that contains the declaration; otherwise, except as a friend declaration, the identifier is declared in the smallest namespace or block scope that contains the declaration. [ Note: These rules also apply within templates. —- end note ] [ Note:
    Other forms of elaborated-type-specifier do not declare a new name,
    and therefore must refer to an existing type-name.
    See
    [basic.lookup.elab] and [dcl.type.elab]. —- end note ]






Here's another interesting bit about it;




dcl.type.elab/1 An
attribute-specifier-seq shall not appear in an elaborated-type-specifier unless the latter is the sole constituent of
a declaration...




That is why this (below) is valid, See it here https://godbolt.org/z/IkmvGn;



void foo()
void* n = (class Boo*)(0);
Boo* b;


class [[deprecated("WTF")]] Mew;


But this, (below) is incorrect1; See it here https://godbolt.org/z/8X1QKq;



void foo()
void* n = (class [[deprecated("WTF")]] Boo*)(0);


class [[deprecated("WTF")]] Mew;



  • 1Strangely, GCC accepts it, but gives this warning:



    attributes ignored on elaborated-type-specifier that is not a forward declaration [-Wattributes]



To see all the other "forms" of elaborated-type-specifier see dcl.type.elab




Edit 2



To double-check my understanding, I asked further, and @Simon Brand made me realise some edge cases, and that is partly hinted by basic.scope/declarative-4.note-2. But chiefly by the second quote in this answer basic.scope/pdecl-7.2



An elaborated-type-specifier within a function-parameter-scope will leak its class-name to the enclosing namespace (note: this is not class-scope).



This is valid https://godbolt.org/z/Fx5B83:



struct Foo 
static void foo(void* = (class Bat*)0); //Leaks it past class-scope
;

void moo()
Bat* m;



Even if you hide it in nested classes https://godbolt.org/z/40Raup:



struct Foo 
class Moo
class Mew
void foo(void* = (class Bat*)0); //
;
;
;

void moo()
Bat* m;



... However, the name stops leaking at the closest namespace https://godbolt.org/z/YDljDo .



namespace zoo 
struct Foo
class Moo
class Mew
void foo(void* = (class Bat*)0);
;
;
;

void moo()
zoo::Bat* m;



Finally,



mem-init-list is a compound-statement and does not have a function-parameter-scope; So, if you want to achieve, the leakage, do the declaration in the function-parameter-scope. See https://godbolt.org/z/CqejYS



struct Foo 
void* p;
Foo(void* = (class Zoo*)(0))
: p(class Bar*)0
Bar* bar;

;

Zoo* m; //Zoo* leaked
//Bar* n //Bar* did not leak





share|improve this answer

























  • But saying class Bar should implicitly forward declare it, no? Even there?

    – Lightness Races in Orbit
    Feb 13 at 10:42







  • 1





    @LightnessRacesinOrbit, If I am not mistaken, the so-called "forward declaration" is actually one of multiple forms of elaborated-type-specifier; And only two forms introduce a name. I'm not sure I understand your comment correctly though....

    – WhiZTiM
    Feb 13 at 11:34












  • Nope, you understood correctly, and also (it seems) answered correctly - thanks :)

    – Lightness Races in Orbit
    Feb 13 at 12:01











  • (Would you consider adding that to the answer? I think it's the key)

    – Lightness Races in Orbit
    Feb 13 at 12:01











  • @LightnessRacesinOrbit, Thanks for the suggestion, done... improved the answer a bit :-)

    – WhiZTiM
    Feb 13 at 18:16













8












8








8







This is about declaration rules and scope rules; (elaborated type specifier may also declare a class-name in an expression).




scope.declarative/3:
The names declared by a declaration are introduced into the scope in
which the declaration occurs, except that the presence of a friend
specifier, certain uses of the elaborated-type-specifier
([dcl.type.elab]), and using-directives ([namespace.udir]) alter this
general behavior.




a constructor has a scope (as eeroika's answer mentions), likewise anything declared therein. That is why this should be valid



struct Foo 
void* p;
Foo() : p(class Bar*)0
Bar* bar;

;


https://godbolt.org/z/m3Tdle



But not:



struct Foo 
void* p;
Foo() : p(class Bar*)0
//Scope of Bar only exists here

;

Bar* bar;


EDIT:
More details about this:



The so-called "forward declaration" in C++ is technically one of multiple "forms" of elaborated-type-specifier; And only two forms may introduce a name. (Emphasis mine)




The point of declaration of a class first declared in an
elaborated-type-specifier is as follows:




  • (7.1) for a declaration of the form



    class-key attribute-specifier-seqopt identifier; //<-- Note the semi colon



    the identifier is declared to be a class-name in the scope that contains
    the declaration, otherwise




  • (7.2) for an elaborated-type-specifier of the form



    class-key identifier



    if the elaborated-type-specifier is used in the decl-specifier-seq or parameter-declaration-clause of a function defined in namespace scope, the identifier is declared as a class-name in the namespace that contains the declaration; otherwise, except as a friend declaration, the identifier is declared in the smallest namespace or block scope that contains the declaration. [ Note: These rules also apply within templates. —- end note ] [ Note:
    Other forms of elaborated-type-specifier do not declare a new name,
    and therefore must refer to an existing type-name.
    See
    [basic.lookup.elab] and [dcl.type.elab]. —- end note ]






Here's another interesting bit about it;




dcl.type.elab/1 An
attribute-specifier-seq shall not appear in an elaborated-type-specifier unless the latter is the sole constituent of
a declaration...




That is why this (below) is valid, See it here https://godbolt.org/z/IkmvGn;



void foo()
void* n = (class Boo*)(0);
Boo* b;


class [[deprecated("WTF")]] Mew;


But this, (below) is incorrect1; See it here https://godbolt.org/z/8X1QKq;



void foo()
void* n = (class [[deprecated("WTF")]] Boo*)(0);


class [[deprecated("WTF")]] Mew;



  • 1Strangely, GCC accepts it, but gives this warning:



    attributes ignored on elaborated-type-specifier that is not a forward declaration [-Wattributes]



To see all the other "forms" of elaborated-type-specifier see dcl.type.elab




Edit 2



To double-check my understanding, I asked further, and @Simon Brand made me realise some edge cases, and that is partly hinted by basic.scope/declarative-4.note-2. But chiefly by the second quote in this answer basic.scope/pdecl-7.2



An elaborated-type-specifier within a function-parameter-scope will leak its class-name to the enclosing namespace (note: this is not class-scope).



This is valid https://godbolt.org/z/Fx5B83:



struct Foo 
static void foo(void* = (class Bat*)0); //Leaks it past class-scope
;

void moo()
Bat* m;



Even if you hide it in nested classes https://godbolt.org/z/40Raup:



struct Foo 
class Moo
class Mew
void foo(void* = (class Bat*)0); //
;
;
;

void moo()
Bat* m;



... However, the name stops leaking at the closest namespace https://godbolt.org/z/YDljDo .



namespace zoo 
struct Foo
class Moo
class Mew
void foo(void* = (class Bat*)0);
;
;
;

void moo()
zoo::Bat* m;



Finally,



mem-init-list is a compound-statement and does not have a function-parameter-scope; So, if you want to achieve, the leakage, do the declaration in the function-parameter-scope. See https://godbolt.org/z/CqejYS



struct Foo 
void* p;
Foo(void* = (class Zoo*)(0))
: p(class Bar*)0
Bar* bar;

;

Zoo* m; //Zoo* leaked
//Bar* n //Bar* did not leak





share|improve this answer















This is about declaration rules and scope rules; (elaborated type specifier may also declare a class-name in an expression).




scope.declarative/3:
The names declared by a declaration are introduced into the scope in
which the declaration occurs, except that the presence of a friend
specifier, certain uses of the elaborated-type-specifier
([dcl.type.elab]), and using-directives ([namespace.udir]) alter this
general behavior.




a constructor has a scope (as eeroika's answer mentions), likewise anything declared therein. That is why this should be valid



struct Foo 
void* p;
Foo() : p(class Bar*)0
Bar* bar;

;


https://godbolt.org/z/m3Tdle



But not:



struct Foo 
void* p;
Foo() : p(class Bar*)0
//Scope of Bar only exists here

;

Bar* bar;


EDIT:
More details about this:



The so-called "forward declaration" in C++ is technically one of multiple "forms" of elaborated-type-specifier; And only two forms may introduce a name. (Emphasis mine)




The point of declaration of a class first declared in an
elaborated-type-specifier is as follows:




  • (7.1) for a declaration of the form



    class-key attribute-specifier-seqopt identifier; //<-- Note the semi colon



    the identifier is declared to be a class-name in the scope that contains
    the declaration, otherwise




  • (7.2) for an elaborated-type-specifier of the form



    class-key identifier



    if the elaborated-type-specifier is used in the decl-specifier-seq or parameter-declaration-clause of a function defined in namespace scope, the identifier is declared as a class-name in the namespace that contains the declaration; otherwise, except as a friend declaration, the identifier is declared in the smallest namespace or block scope that contains the declaration. [ Note: These rules also apply within templates. —- end note ] [ Note:
    Other forms of elaborated-type-specifier do not declare a new name,
    and therefore must refer to an existing type-name.
    See
    [basic.lookup.elab] and [dcl.type.elab]. —- end note ]






Here's another interesting bit about it;




dcl.type.elab/1 An
attribute-specifier-seq shall not appear in an elaborated-type-specifier unless the latter is the sole constituent of
a declaration...




That is why this (below) is valid, See it here https://godbolt.org/z/IkmvGn;



void foo()
void* n = (class Boo*)(0);
Boo* b;


class [[deprecated("WTF")]] Mew;


But this, (below) is incorrect1; See it here https://godbolt.org/z/8X1QKq;



void foo()
void* n = (class [[deprecated("WTF")]] Boo*)(0);


class [[deprecated("WTF")]] Mew;



  • 1Strangely, GCC accepts it, but gives this warning:



    attributes ignored on elaborated-type-specifier that is not a forward declaration [-Wattributes]



To see all the other "forms" of elaborated-type-specifier see dcl.type.elab




Edit 2



To double-check my understanding, I asked further, and @Simon Brand made me realise some edge cases, and that is partly hinted by basic.scope/declarative-4.note-2. But chiefly by the second quote in this answer basic.scope/pdecl-7.2



An elaborated-type-specifier within a function-parameter-scope will leak its class-name to the enclosing namespace (note: this is not class-scope).



This is valid https://godbolt.org/z/Fx5B83:



struct Foo 
static void foo(void* = (class Bat*)0); //Leaks it past class-scope
;

void moo()
Bat* m;



Even if you hide it in nested classes https://godbolt.org/z/40Raup:



struct Foo 
class Moo
class Mew
void foo(void* = (class Bat*)0); //
;
;
;

void moo()
Bat* m;



... However, the name stops leaking at the closest namespace https://godbolt.org/z/YDljDo .



namespace zoo 
struct Foo
class Moo
class Mew
void foo(void* = (class Bat*)0);
;
;
;

void moo()
zoo::Bat* m;



Finally,



mem-init-list is a compound-statement and does not have a function-parameter-scope; So, if you want to achieve, the leakage, do the declaration in the function-parameter-scope. See https://godbolt.org/z/CqejYS



struct Foo 
void* p;
Foo(void* = (class Zoo*)(0))
: p(class Bar*)0
Bar* bar;

;

Zoo* m; //Zoo* leaked
//Bar* n //Bar* did not leak






share|improve this answer














share|improve this answer



share|improve this answer








edited Feb 14 at 10:13

























answered Feb 12 at 12:36









WhiZTiMWhiZTiM

18.1k33052




18.1k33052












  • But saying class Bar should implicitly forward declare it, no? Even there?

    – Lightness Races in Orbit
    Feb 13 at 10:42







  • 1





    @LightnessRacesinOrbit, If I am not mistaken, the so-called "forward declaration" is actually one of multiple forms of elaborated-type-specifier; And only two forms introduce a name. I'm not sure I understand your comment correctly though....

    – WhiZTiM
    Feb 13 at 11:34












  • Nope, you understood correctly, and also (it seems) answered correctly - thanks :)

    – Lightness Races in Orbit
    Feb 13 at 12:01











  • (Would you consider adding that to the answer? I think it's the key)

    – Lightness Races in Orbit
    Feb 13 at 12:01











  • @LightnessRacesinOrbit, Thanks for the suggestion, done... improved the answer a bit :-)

    – WhiZTiM
    Feb 13 at 18:16

















  • But saying class Bar should implicitly forward declare it, no? Even there?

    – Lightness Races in Orbit
    Feb 13 at 10:42







  • 1





    @LightnessRacesinOrbit, If I am not mistaken, the so-called "forward declaration" is actually one of multiple forms of elaborated-type-specifier; And only two forms introduce a name. I'm not sure I understand your comment correctly though....

    – WhiZTiM
    Feb 13 at 11:34












  • Nope, you understood correctly, and also (it seems) answered correctly - thanks :)

    – Lightness Races in Orbit
    Feb 13 at 12:01











  • (Would you consider adding that to the answer? I think it's the key)

    – Lightness Races in Orbit
    Feb 13 at 12:01











  • @LightnessRacesinOrbit, Thanks for the suggestion, done... improved the answer a bit :-)

    – WhiZTiM
    Feb 13 at 18:16
















But saying class Bar should implicitly forward declare it, no? Even there?

– Lightness Races in Orbit
Feb 13 at 10:42






But saying class Bar should implicitly forward declare it, no? Even there?

– Lightness Races in Orbit
Feb 13 at 10:42





1




1





@LightnessRacesinOrbit, If I am not mistaken, the so-called "forward declaration" is actually one of multiple forms of elaborated-type-specifier; And only two forms introduce a name. I'm not sure I understand your comment correctly though....

– WhiZTiM
Feb 13 at 11:34






@LightnessRacesinOrbit, If I am not mistaken, the so-called "forward declaration" is actually one of multiple forms of elaborated-type-specifier; And only two forms introduce a name. I'm not sure I understand your comment correctly though....

– WhiZTiM
Feb 13 at 11:34














Nope, you understood correctly, and also (it seems) answered correctly - thanks :)

– Lightness Races in Orbit
Feb 13 at 12:01





Nope, you understood correctly, and also (it seems) answered correctly - thanks :)

– Lightness Races in Orbit
Feb 13 at 12:01













(Would you consider adding that to the answer? I think it's the key)

– Lightness Races in Orbit
Feb 13 at 12:01





(Would you consider adding that to the answer? I think it's the key)

– Lightness Races in Orbit
Feb 13 at 12:01













@LightnessRacesinOrbit, Thanks for the suggestion, done... improved the answer a bit :-)

– WhiZTiM
Feb 13 at 18:16





@LightnessRacesinOrbit, Thanks for the suggestion, done... improved the answer a bit :-)

– WhiZTiM
Feb 13 at 18:16

















draft saved

draft discarded
















































Thanks for contributing an answer to Stack Overflow!


  • Please be sure to answer the question. Provide details and share your research!

But avoid


  • Asking for help, clarification, or responding to other answers.

  • Making statements based on opinion; back them up with references or personal experience.

To learn more, see our tips on writing great answers.




draft saved


draft discarded














StackExchange.ready(
function ()
StackExchange.openid.initPostLogin('.new-post-login', 'https%3a%2f%2fstackoverflow.com%2fquestions%2f54649661%2fwill-a-class-that-is-declared-within-a-member-initializer-of-a-constructor-of-an%23new-answer', 'question_page');

);

Post as a guest















Required, but never shown





















































Required, but never shown














Required, but never shown












Required, but never shown







Required, but never shown

































Required, but never shown














Required, but never shown












Required, but never shown







Required, but never shown






Popular posts from this blog

How to check contact read email or not when send email to Individual?

Bahrain

Postfix configuration issue with fips on centos 7; mailgun relay