Will a class that is declared within a member initializer of a constructor of another class be visible outside it?
Clash Royale CLAN TAG#URR8PPP
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
|
show 5 more comments
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
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
|
show 5 more comments
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
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
c++ language-lawyer
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
|
show 5 more comments
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
|
show 5 more comments
2 Answers
2
active
oldest
votes
[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].
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
|
show 9 more comments
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
But sayingclass 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 ofelaborated-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
|
show 5 more comments
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
);
);
Sign up or log in
StackExchange.ready(function ()
StackExchange.helpers.onClickDraftSave('#login-link');
);
Sign up using Google
Sign up using Facebook
Sign up using Email and Password
Post as a guest
Required, but never shown
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
[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].
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
|
show 9 more comments
[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].
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
|
show 9 more comments
[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].
[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].
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
|
show 9 more comments
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
|
show 9 more comments
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
But sayingclass 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 ofelaborated-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
|
show 5 more comments
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
But sayingclass 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 ofelaborated-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
|
show 5 more comments
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
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
edited Feb 14 at 10:13
answered Feb 12 at 12:36
WhiZTiMWhiZTiM
18.1k33052
18.1k33052
But sayingclass 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 ofelaborated-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
|
show 5 more comments
But sayingclass 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 ofelaborated-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
|
show 5 more comments
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.
Sign up or log in
StackExchange.ready(function ()
StackExchange.helpers.onClickDraftSave('#login-link');
);
Sign up using Google
Sign up using Facebook
Sign up using Email and Password
Post as a guest
Required, but never shown
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
Sign up or log in
StackExchange.ready(function ()
StackExchange.helpers.onClickDraftSave('#login-link');
);
Sign up using Google
Sign up using Facebook
Sign up using Email and Password
Post as a guest
Required, but never shown
Sign up or log in
StackExchange.ready(function ()
StackExchange.helpers.onClickDraftSave('#login-link');
);
Sign up using Google
Sign up using Facebook
Sign up using Email and Password
Post as a guest
Required, but never shown
Sign up or log in
StackExchange.ready(function ()
StackExchange.helpers.onClickDraftSave('#login-link');
);
Sign up using Google
Sign up using Facebook
Sign up using Email and Password
Sign up using Google
Sign up using Facebook
Sign up using Email and Password
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
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