At work, I'm experimenting a bit to bring some reflection into our codebase. Basically what I want to achieve, is to capture a pointer to data-member inside the type of the data-member's initializer:
template<class Class, int Class::*dataMember>
struct Reflect
{
operator int() {return 0;}
};
class Foo
{
public:
int bar = Reflect<Foo, &Foo::bar>{};
};
Although clang 3.4.1 (http://gcc.godbolt.org/) and Intel C++ XE 14.0 are able to compile this piece of code, when using MSVC12 I get the following error message:
error C2065: 'bar' : undeclared identifier
error C2975: 'dataMember' : invalid template argument for 'Reflect', expected compile-time constant expression
Furthermore, gcc 4.9.2 also seems to have trouble with it: http://ideone.com/ZUVOMO.
So my questions are:
Is the above piece of code valid C++11?
If yes, are there any work arounds for the failing compilers?
What VC++ complains about is certainly not a problem; [basic.scope.pdecl]/1,6:
The point of declaration for a name is immediately after its complete
declarator (Clause 8) and before its initializer (if any), except as
noted below.[…]
After the point of declaration of a class member, the member name can
be looked up in the scope of its class.
This implies that the name lookup is fine. However, as pointed out by #hvd in the comments, there are certain ambiguities in the grammar of such constructs.
Presumably GCC parses the above line until the comma:
int bar = Reflect<Foo,
// at this point Reflect < Foo can be a perfectly fine relational-expression.
// stuff after the comma could be a declarator for a second member.
And bails out once the rest is encountered.
A workaround that makes GCC happy is
int bar = decltype( Reflect<Foo, &Foo::bar>{} )();
Demo. This does not help with VC++ though, which apparently confuses the point of declaration as indicated by the error message.
Thus moving the initializer into a constructor will work:
int bar;
Foo() : bar( Reflect<Foo, &Foo::bar>{} ) {}
// (also works for GCC)
... while providing an initializer at the declaration of bar cannot. Demo #2 on rextester.
Related
Consider the following code:
#include <type_traits>
struct outer {
struct inner {
unsigned int x = 0;
};
// static_assert(std::is_default_constructible<inner>::value,
// "not default ctorable - inside");
};
static_assert(std::is_default_constructible<outer::inner>::value,
"not default ctorable - outside");
This compiles fine. But - if I uncomment the static assert inside outer - both asserts fail with clang++ and gcc++. Why should they not both pass?
Notes:
The inner class is, in fact, complete, at the point of the first static assertion. The assertion does not fail due to incompleteness (a failure which produces a specific error message about incompleteness).
Unlike in the related Why is my class non default-constructible? , here - there are no templates, so there is no instantiation-before- the-definition.
If you remove the initializer of x and enable the assertions, the code compiles. (This is also unlike the related question.)
This:
#include <type_traits>
struct outer {
struct inner {
unsigned int x = 0;
};
inner get_an_inner() {
static_assert(std::is_default_constructible<outer::inner>::value,
"not default ctorable - outside");
return inner{};
}
};
compiles!
If we add an explicit default constructor, constexpr inner() {} - the program compiles.
There's a LLVM bug report about basically the same thing, but it's not just clang and the bug report has not gotten any comments.
Updates:
Have submitted GCC bug 102199 (against libstdc++).
Have commented on the LLVM bug, let's see what happens there.
The completeness status of member classes is complicated. Because their member functions are allowed to do forward lookup into the surrounding class scope, those member functions are treated as not yet being defined when processing the (rest of) the containing class, despite the fact that the most basic notion of completeness is plainly satisfied:
struct A {
struct B {};
B b; // OK
};
There are various open issues in this general area: CWG2335 and CWG1360, for instance.
First, why the inner static assert causes the outer static assert to fail? That's because templates are instantiated only once. Once std::is_default_constructible<inner>::value is false, it will remain false, even if default constructibility somehow changes. So the outer static_assert is not very interesting. Why does the inner static_assert fail?
In order to understand this, let's simplify this to the bare minimum. No templates or standard library!
struct outer {
struct inner {
unsigned int x = 0;
};
static constexpr inner i{};
};
This fails with a mysterious compiler message.
$ g++ -c dc.cpp
dc.cpp:6:30: error: default member initializer for ‘outer::inner::x’ required before the end of its enclosing class
8 | static constexpr inner i{};
| ^
dc.cpp:3:24: note: defined here
5 | unsigned int x = 0;
| ^~~~
$ clang++ -c dc.cpp
dc.cpp:6:30: error: default member initializer for 'x' needed within definition of enclosing class 'outer' outside of member functions
static constexpr inner i{};
^
dc.cpp:3:22: note: default member initializer declared here
unsigned int x = 0;
^
1 error generated.
So the compiler indeed cannot default-construct inner where needed, but why?
A quick search on the text of the error message yields this. The accepted answer classes it as a gcc and clang bug, but I'm not convinced. The clang bug report says "Then we would only reject the cases where there's an actual dependency cycle", but the standard doesn't seem to require dependency cycle detection anywhere. This could be a defect in the standard rather than a compiler bug.
Consider the following code:
#include <type_traits>
struct outer {
struct inner {
unsigned int x = 0;
};
// static_assert(std::is_default_constructible<inner>::value,
// "not default ctorable - inside");
};
static_assert(std::is_default_constructible<outer::inner>::value,
"not default ctorable - outside");
This compiles fine. But - if I uncomment the static assert inside outer - both asserts fail with clang++ and gcc++. Why should they not both pass?
Notes:
The inner class is, in fact, complete, at the point of the first static assertion. The assertion does not fail due to incompleteness (a failure which produces a specific error message about incompleteness).
Unlike in the related Why is my class non default-constructible? , here - there are no templates, so there is no instantiation-before- the-definition.
If you remove the initializer of x and enable the assertions, the code compiles. (This is also unlike the related question.)
This:
#include <type_traits>
struct outer {
struct inner {
unsigned int x = 0;
};
inner get_an_inner() {
static_assert(std::is_default_constructible<outer::inner>::value,
"not default ctorable - outside");
return inner{};
}
};
compiles!
If we add an explicit default constructor, constexpr inner() {} - the program compiles.
There's a LLVM bug report about basically the same thing, but it's not just clang and the bug report has not gotten any comments.
Updates:
Have submitted GCC bug 102199 (against libstdc++).
Have commented on the LLVM bug, let's see what happens there.
The completeness status of member classes is complicated. Because their member functions are allowed to do forward lookup into the surrounding class scope, those member functions are treated as not yet being defined when processing the (rest of) the containing class, despite the fact that the most basic notion of completeness is plainly satisfied:
struct A {
struct B {};
B b; // OK
};
There are various open issues in this general area: CWG2335 and CWG1360, for instance.
First, why the inner static assert causes the outer static assert to fail? That's because templates are instantiated only once. Once std::is_default_constructible<inner>::value is false, it will remain false, even if default constructibility somehow changes. So the outer static_assert is not very interesting. Why does the inner static_assert fail?
In order to understand this, let's simplify this to the bare minimum. No templates or standard library!
struct outer {
struct inner {
unsigned int x = 0;
};
static constexpr inner i{};
};
This fails with a mysterious compiler message.
$ g++ -c dc.cpp
dc.cpp:6:30: error: default member initializer for ‘outer::inner::x’ required before the end of its enclosing class
8 | static constexpr inner i{};
| ^
dc.cpp:3:24: note: defined here
5 | unsigned int x = 0;
| ^~~~
$ clang++ -c dc.cpp
dc.cpp:6:30: error: default member initializer for 'x' needed within definition of enclosing class 'outer' outside of member functions
static constexpr inner i{};
^
dc.cpp:3:22: note: default member initializer declared here
unsigned int x = 0;
^
1 error generated.
So the compiler indeed cannot default-construct inner where needed, but why?
A quick search on the text of the error message yields this. The accepted answer classes it as a gcc and clang bug, but I'm not convinced. The clang bug report says "Then we would only reject the cases where there's an actual dependency cycle", but the standard doesn't seem to require dependency cycle detection anywhere. This could be a defect in the standard rather than a compiler bug.
I have a simple code snippet shown below (https://godbolt.org/z/cPT3PhYdj):
int main() {
int x = 1;
auto(1); // ok in GCC, error in Clang
auto{1}; // ok in GCC, error in Clang
static_cast<void>(auto(x)); // ok
auto{x}; // ok in GCC, error in Clang
auto(x); // both error in GCC an Clang
}
Where both GCC and Clang emit an error showing:
// by GCC Trunk [-std=c++23]
<source>: In function 'int main()':
<source>:7:3: error: declaration of 'auto x' has no initializer
7 | auto(x); // why
| ^~~~
Compiler returned: 1
// by Clang Trunk [-std=c++2b]
<source>:3:8: error: expected unqualified-id
auto(1); // why
^
<source>:3:8: error: expected ')'
<source>:3:7: note: to match this '('
auto(1); // why
^
<source>:4:7: error: expected unqualified-id
auto{1}; // why
^
<source>:6:7: error: expected unqualified-id
auto{x}; // why
^
<source>:7:8: error: redefinition of 'x'
auto(x); // why
^
<source>:2:7: note: previous definition is here
int x = 1;
^
<source>:7:8: error: declaration of variable 'x' with deduced type 'auto' requires an initializer
auto(x); // why
^
6 errors generated.
Compiler returned: 1
If C++23 is experimental, and will they be able to fix the ambiguity or change the disambiguation since another auto(expr) is introduced, or just leave it be?
Are these expressions supposed to be parsed as explicit type decay conversion auto(expr) or auto{expr} in expression statements or parsed as a declaration?
If there is no ambiguity, then which priority comes first:
auto(identifier) as auto identifier?, or
auto(identifier) as cast expression?
From Explicit cast conversion:
auto ( expression ) (8) (since C++23)
auto { expression } (9) (since C++23)
8,9) The auto specifier is replaced with the deduced type of the invented variable x declared with auto x(expression); (which is never interpreted as a function declaration) or auto x{expression}; respectively. The result is always a prvalue of an object type.
So your usage seems to be allowed(in accordance with ) by the above quoted statement.
Here is a working demo of your code. Note in the linked demo, only the usage auto(x) produces an error, all other cases work fine.
Also note that from PR105516:
auto(x); is interpreted as auto x;. Use +auto(x); if you want that to be an expression.
If there is no ambiguity, then which priority comes first:
This is one of the examples showing how C++ is a context sensitive language. A construct cannot always be understood without knowing its wider contexts. Consider the following example:
int main()
{
int x = 0 ;
int k ;
//------vvvvvvv----->here auto(x) is behaves as an expression as it is used as an expression
k = auto(x) ;
auto(p) ; //this a declaration and not an explicit case unlike the above
}
As far as I can tell the paper introducing this feature didn't make any further relevant changes except to allow auto as type specifier in a functional-style explicit cast.
So intuitively auto here should behave the same as any other type specifier would.
For example, replacing auto with int, it is expected that all of these cases work and are functional-style explicit casts, except
int(x); // auto(x);
This one could according to the grammar also be a declaration of a variable named x of type int (or placeholder type) with parentheses around the declarator and without initializer.
As usual, the grammar is disambiguated by preferring the interpretation as declaration (see [stmt.ambig]/1). At least, I don't think that it should be different for auto. A declaration with auto placeholder type requires an initializer, which is not present in this interpretation, but according to [stmt.ambig]/3 the disambiguation is supposed to be done very early and purely syntactic even if it ends up in an ill-formed declaration. I am not completely sure but I guess this should mean that auto(x); should still be disambiguated as a declaration.
I don't know why Clang gives errors for many of the other lines. I suppose the feature is either implemented only partially or these are implementation bugs (it is a very new feature in an unfinished standard revision).
I think auto(x) (where x is literally an identifier) can still have different meanings in different contexts and thus is subject to [dcl.ambig.res] in C++23.
Unfortunately, the ambiguity is unlikely to be fixed. In the following program (well-formed since C++11), auto(x) = Bar{}; is interpreted as a declaration. If auto(x) were "fixed", auto(x) = Bar{}; would become an expression statement, which would change the existing behavior.
#include <cstdio>
struct Bar {
Bar()
{
std::puts("initialization");
}
};
struct Foo {
void operator=(Bar)
{
std::puts("assignment");
}
};
int main()
{
Foo x{};
{
auto(x) = Bar{}; // only prints "initialization"
}
}
The following code, which loosely represents some serialization stuff I'm working on, compiles with g++ (http://ideone.com/0rsGmt), but Visual Studio Express 2013 RC fails with the following errors:
Error 1 error C2326: 'void foo::print(void)' : function cannot access 'foo::bar::member_'
Error 2 error C2039: 'bar' : is not a member of 'foo'
The code:
#include <iostream>
class foo
{
private:
struct bar
{
int member_;
};
public:
void print()
{
std::cout << sizeof(decltype(foo::bar::member_)) << std::endl;
}
};
int main(int argc, char* argv[])
{
foo f;
f.print();
return 0;
}
What's wrong? Visual Studio inadequacy or something else? Obviously I can move the struct declaration out of the class; and Daniel Frey has offered a workaround below; but I want to know why the above code won't compile as-is with Visual Studio.
Update: The accepted answer says that it should work, but as is typical for Microsoft it doesn't. I've filled a bug report here: http://connect.microsoft.com/VisualStudio/feedback/details/801829/incomplete-decltype-support-in-c-11-compiler
(If someone can suggest a better title for the question, I'd appreciate it!)
I think your code should work (as on GCC or Clang), according to
5 Expressions [expr]
8 In some contexts, unevaluated operands appear (5.2.8, 5.3.3, 5.3.7, 7.1.6.2). An unevaluated operand is not evaluated. An unevaluated operand is considered a full-expression. [Note: In an unevaluated operand, a non-static class member may be named (5.1) and naming of objects or functions does not, by itself, require that a definition be provided (3.2). — end note ]
It seems that VC++ does not implement what the note clarifies, so you need a (faked) instance as a work-around to make VC++ happy. This should work:
void print()
{
std::cout << sizeof(std::declval<bar>().member_) << std::endl;
}
Note that I removed the decltype as sizeof can work on expressions directly.
Maybe the problem with your code is that structs are like classes and you define the struct inside the class but the compiler does not know that this class(or struct) exists until it compiles the whole class.
The code below does not compile in Visual C++ 2005.
class SomeClass {
public: boost::function<void()> func;
SomeClass(boost::function<void()> &func): func(func) { }
};
void someFunc() {
std::cout << "someFunc" << std::endl;
}
int main() {
SomeClass sc(boost::function<void()>(&someFunc));
sc.func(); // error C2228: left of '.func' must have class/struct/union
return 0;
}
If I put parentheses around the argument to the SomeClass constructor or constructs the boost::function object outside the argument list it compiles fine.
SomeClass sc((boost::function<void()>(&someFunc)));
// or
boost::function<void()> f(&someFunc);
SomeClass sc(f);
What is the problem with the previous code?
It's a function declaration for a function taking a reference to a boost:function <void()> and returning a SomeClass. You can memorize the following rule, which turns out to apply to many other such disambiguation cases. You can find descriptions of these cases in section 8.2 of the C++ Standard.
Any construct that could possibly be a declaration will be taken as a declaration
That means, the following will be taken as a parameter declaration, with superfluous parentheses
boost::function<void()>(&someFunc)
If you remove the parentheses, this will become clear
boost::function<void()> &someFunc
And thus, the whole declaration will not anymore declare an object, but a function
SomeClass sc(boost::function<void()> &someFunc);
To fix it, use the cast-notation
SomeClass sc((boost::function<void()>)&someFunc);
Or put parentheses around the whole expression, like you did.
Here is the Standard in all its glory from 8.2:
The ambiguity arising from the similarity between a function-style cast and a declaration mentioned in 6.8 can also occur in the context of a declaration. In that context, the choice is between a function declaration with a redundant set of parentheses around a parameter name and an object declaration with a function-style cast as the initializer. Just as for the ambiguities mentioned in 6.8, the resolution is to consider any construct that could possibly be a declaration a declaration. [Note: a declaration can be explicitly disambiguated by a nonfunction-style cast, by a = to indicate initialization or by removing the redundant parentheses around the parameter name. ]
Note that for controlling precedence, you are allowed to introduce parentheses just about anywhere, like in the following
int (((((((a))))))) = 3;
int (*(pa)) = &a;
This is known as "C++'s Most Vexing Parse" (from a book by Scott Meyers called Effective STL).
As answered above, the compiler prefers to interpret the problematic line as a function declaration.