C++11 - Declaring Non-Static Data Members as 'Auto'

C++11 - declaring non-static data members as 'auto'

The rule for prohibiting non-static members is in 7.1.6.4 clause 4:

The auto type-specifier can also be used in declaring a variable in
the condition of a selection statement (6.4) or an iteration statement
(6.5), in the type-specifier-seq in the new-type-id or type-id of a
new-expression (5.3.4), in a for-range-declaration, and in declaring a
static data member with a brace-or-equal-initializer that appears within the member-specification of a class definition (9.4.2).

I found the rationale for it being static here which reflects how James McNellis explains it in the comment.

One national body dislikes allowing the auto type-specifier for
non-statics. From an e-mail to the authors:

    template< class T >
struct MyType : T {
auto data = func();
static const size_t erm = sizeof(data);
};

In order to determine the layout of X, we now have 2-phase name lookup and ADL. Note that func could be either a type or a function;
it may be found in T, the namespace of MyType, the associated
namespace(s) of T when instantiated, the global namespace, an
anonymous namespace, or any namespaces subject to a using directive.
With care we could probably throw some concept_map lookup in for luck.
Depending on the order of header inclusion I might even get different results for ADL, and break the One Definition Rule - which
is not required to be diagnosed.

Because of this controversy, the authors no longer propose that auto
be allowed for non-static data members.

So, basically depending on the order of header inclusion, the type of data could be very different. Of course, auto x = 5; would not need to depend on 2-phase name lookup or ADL, however, I'm a assuming that they made it a "blanket" rule because otherwise, they would have to make individual rules for every use case which would make things very complicated.

In the same paper, the author proposes eliminating this restriction, however, it seems this proposal has been rejected probably due to the above rationale and also so that expected behavior can be the same no matter what the initializer is.

Error: non-static data member declared ‘auto’

You could use std::function to simplify this.

#include <functional>

struct State {
string id;
std::function<void()> action;
string next;

State(){id = "";}
State(string _id, std::function<void()> func, string n = "") :
id(_id), action(func), next(n) {}
};
class StateMachine {
//...
void addState(string id, std::function<void()> func) {
if(activeState == "") activeState = id;
stateList.emplace(id, State{id, func});
}
//...

Why doesn't the C++11 'auto' keyword work for static members?

It's disallowed by the language:

[C++11: 7.1.6.4]:

1 The auto type-specifier signifies that the type of a variable being declared shall be deduced from its initializer or that a function declarator shall include a trailing-return-type.

2 The auto type-specifier may appear with a function declarator with a trailing-return-type (8.3.5) in any context where such a declarator is valid.

3 Otherwise, the type of the variable is deduced from its initializer. The name of the variable being declared shall not appear in the initializer expression. This use of auto is allowed when declaring variables in a block (6.3), in namespace scope (3.3.6), and in a for-init-statement (6.5.3). auto shall appear as one of the decl-specifiers in the decl-specifier-seq and the decl-specifier-seq shall be followed by one or more init-declarators, each of which shall have a non-empty initializer.

4 The auto type-specifier can also be used in declaring a variable in the condition of a selection statement (6.4) or an iteration statement (6.5), in the type-specifier-seq in the new-type-id or type-id of a new-expression (5.3.4), in a for-range-declaration, and in declaring a static data member with a brace-or-equal-initializer that appears within the member-specification of a class definition (9.4.2).

5 A program that uses auto in a context not explicitly allowed in this section is ill-formed.

It's hard to prove a negative, but there's simply no explicit rule in the standard to allow auto in your case.

However, the same rules mean that the following is valid:

struct Foo {
static constexpr auto constant_string = "foo";
};

int main() {}

(Note that the type of Foo::constant_string is char const* const rather than, say, char const[3]; this is an effect of using auto.)

Is it possible to have an auto member variable?

You can, but you have to declare it static and const:

struct Timer {
static const auto start = 0;
};

A working example in Coliru.

With this limitation, you therefore cannot have start as a non-static member, and cannot have different values in different objects.

If you want different types of start for different objects, better have your class as a template

template<typename T>
struct Timer {
T start;
};

If you want to deduce the type of T, you can make a factory-like function that does the type deduction.

template<typename T>
Timer<typename std::decay<T>::type> MakeTimer(T&& startVal) { // Forwards the parameter
return Timer<typename std::decay<T>::type>{std::forward<T>(startVal)};
}

Live example.

Invalid use of non-static data member when initializing static member from global variable

This is a peculiar language quirk - the scope resolution on the left, in int A::i, affects the lookup scope on the right, so that actually refers to the x member of A.

Either rename one of the variables, or specify the scope of the desired x explicitly:

int A::i = ::x;

Is the cppreference definition of non-static data member wrong?

So their definition of non-static data member is wrong

Yes, it was wrong to use the word "variable" in the introductory sentence of the data members page (and, as mentioned in the comment, it's a wiki, the discussion tabs on wiki pages get faster feedback).

The current standard wording is 3[basic]/6 and :

A variable is introduced by the declaration of a reference other than a non-static data member or of an object. The variable’s name, if any, denotes the reference or object.

So, reference data members are excluded explicitly, and to figure out the rest you need the definition of "object" from 1.8[intro.object]/1

An object is created by a definition (3.1), by a new-expression (5.3.4), when implicitly changing the active member of a union (9.3), or when a temporary object is created (4.4, 12.2).

And finally 3.1[basic.def]/2

A declaration is a definition unless ... it declares a non-inline static data member in a class definition (9.2, 9.2.3),

Although it may seem like the distinction between variables and data members is impractical language-lawyerism, it is actually important when understanding compiler diagnostics, at least in this case:

struct X {
int m;
void f() { auto l = [m](){ return m; }; }
};

gcc:

error: capture of non-variable 'X::m' 

clang:

error: 'm' in capture list does not name a variable

icc:

error: member "X::m" is not a variable

C++11 non-static data member uniform initialization fails for pointers to other classes of same base class

Stripping one layer of inheritance:

struct m {
struct state { state *s;
state() : s(0) {};
state(state &s) : s(&s) {}
set(state &s) { this->s = &s; }
};

struct s1 : state {} A; // calls s1(), the default constructor
struct s2 : state {} B // calls s2(), ditto
, C{B} // calls s2(const s2&), the default copy constructor
, D{A}; // calls s2(const s1&)

m() { B.set(A); } // The m() body runs after all the members are constructed.
} M;

You're getting the construction error because your substates declare no constructors so they're getting the compiler-provided defaults, and there is none from a sibling- or base- class reference (the compiler doesn't provide s2(s1&) or s2(state&)).

You're getting the wrong superstate for C because C{B} invokes the default copy constructor s2(s2&), which runs before m()'s body.

Here's what you want instead:

struct m {
struct state { state *s; state() : s(0) {} state(state &s) : s(&s) {} };
struct s1 : state {} A; // default-constructs, fine
struct s2 : state {
s2(state &s) : state(s) {}
s2(s2&s) : state(s) {}
} B // default-constructs
, C{B} // calls s2(s2&), invokes state(state&)
, D{A}; // calls s2(state&)
;
m() : B(A) {};
} M;

When M's constructor runs, first its base classes (there are none) then its members are constructed in declaration order using the specified initializations. There's only one: B(A), so all the rest are defaulted. After all the bases and members are constructed, then the object constructor's body runs.

Why does VC++ 2013 not support non-static data member initializers as promised

What's the root cause? Is it a compiler bug?

It's a documentation bug, both here and chris's link in the comment say that Non-static data member initializers will be implemented in VS2013 RTM.

Don't be surprised when you find false info on MSDN, there is a lot of old material, etc. which no one has updated or somebody made a mistake when typing it in.



Related Topics



Leave a reply



Submit