Why Doesn't the C++11 'Auto' Keyword Work for Static Members

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.)

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.

auto keyword strange behavior in C++11

The auto storage class specifier is not "useless and deprecated in C++11," it has been removed entirely. The auto keyword is no longer a storage class specifier and cannot be used as one.

In C++11, auto is a simple type specifier.

Why can't we use auto in template static member initialization?

The declaration of a variable with a placeholder auto/decltype(auto) must have an initializer for the type of the variable to be deduced:

The type of a variable declared using auto or decltype(auto) is deduced from its initializer. This use 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 or decltype(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.

Your example doesn't have an initializer for the definition, so your code is ill-formed.

The definition of a static member may have a placeholder, but the problem in your example has to do with the fact that you're using templates and the declaration of the static member is dependent on the template argument. For example, this definition won't compile:

template <typename MemberType>
auto Holder<MemberType>::member = 42; // error: redefinition of 'member' with
// a different type: 'int' vs 'MemberType'

Holder<MemberType> causes an implicit instantiation where the compiler will look at the declaration of Holder<MemberType>::member. The compiler doesn't know at the time of this instantiation whether the type deduced from the initializer (int) matches MemberType, which is why you get the strange error "int vs MemberType".

The above code works if you create an explicit specialization of the data member because MemberType is known at the time of the instantiation:

template <>
auto Holder<int>::member = 42; // OK, decltype(42) matches int (MemberType)

Is there any reason to use the 'auto' keyword in C++03?

auto is a storage class specifier, static, register and extern too. You can only use one of these four in a declaration.

Local variables (without static) have automatic storage duration, which means they live from the start of their definition until the end of their block. Putting auto in front of them is redundant since that is the default anyway.

I don't know of any reason to use it in C++. In old C versions that have the implicit int rule, you could use it to declare a variable, like in:

int main(void) { auto i = 1; }

To make it valid syntax or disambiguate from an assignment expression in case i is in scope. But this doesn't work in C++ anyway (you have to specify a type). Funny enough, the C++ Standard writes:

An object declared without a storage-class-specifier at block scope or declared as a function parameter has automatic storage duration by default. [Note: hence, the auto specifier is almost always redundant and not often used; one use of auto is to distinguish a declaration-statement from an expression-statement (6.8) explicitly. — end note]

which refers to the following scenario, which could be either a cast of a to int or the declaration of a variable a of type int having redundant parentheses around a. It is always taken to be a declaration, so auto wouldn't add anything useful here, but would for the human, instead. But then again, the human would be better off removing the redundant parentheses around a, I would say:

int(a);

With the new meaning of auto arriving with C++0x, I would discourage using it with C++03's meaning in code.

C++ auto keyword. Why is it magic?

auto was a keyword that C++ "inherited" from C that had been there nearly forever, but virtually never used because there were only two possible conditions: either it wasn't allowed, or else it was assumed by default.

The use of auto to mean a deduced type was new with C++11.

At the same time, auto x = initializer deduces the type of x from the type of initializer the same way as template type deduction works for function templates. Consider a function template like this:

template<class T>
int whatever(T t) {
// point A
};

At point A, a type has been assigned to T based on the value passed for the parameter to whatever. When you do auto x = initializer;, the same type deduction is used to determine the type for x from the type of initializer that's used to initialize it.

This means that most of the type deduction mechanics a compiler needs to implement auto were already present and used for templates on any compiler that even sort of attempted to implement C++98/03. As such, adding support for auto was apparently fairly easy for essentially all the compiler teams--it was added quite quickly, and there seem to have been few bugs related to it either.

When this answer was originally written (in 2011, before the ink was dry on the C++ 11 standard) auto was already quite portable. Nowadays, it's thoroughly portable among all the mainstream compilers. The only obvious reasons to avoid it would be if you need to write code that's compatible with a C compiler, or you have a specific need to target some niche compiler that you know doesn't support it (e.g., a few people still write code for MS-DOS using compilers from Borland, Watcom, etc., that haven't seen significant upgrades in decades). If you're using a reasonably current version of any of the mainstream compilers, there's no reason to avoid it at all though.

More recent revisions of the standard have added a few new places that auto can be used. Starting with C++14, you can use auto for the type of a parameter to a lambda:

    [](auto s) { return s + 1; }

This does essentially the same thing as the example above--even though it doesn't explicitly use template syntax, this is basically a template that deduces the type of the parameter, and instantiates the template over that type.

That was convenient and useful enough that in C++20, the same capability was added for normal functions, not just lambdas.

But, just as before all of this really comes down to using the same basic type deduction mechanism as we've had for function templates since C++98. auto allows that to be used in more places, and more conveniently, but the underlying heavy lifting remains the same.

Why does auto a=1; compile in C?

auto is an old C keyword that means "local scope". auto a is the same as auto int a, and because local scope is the default for a variable declared inside a function, it's also the same as int a in this example.

This keyword is actually a leftover from C's predecessor B, where there were no base types: everything was int, pointer to int, array of int.(*) Declarations would be either auto or extrn [sic]. C inherited the "everything is int" as a default rule, so you could declare integers with

auto a;
extern b;
static c;

ISO C got rid of this, but many compilers still accept it for backward compatibility. If it seems unfamiliar, then you should realise that a related rule is at work in

unsigned d;  // actually unsigned int

which is still common in modern code.

C++11 reused the keyword, which few if any C++ programmers were using with the original meaning, for its type inference. This is mostly safe because the "everything is int" rule from C had already been dropped in C++98; the only thing that breaks is auto T a, which no-one was using anyway. (Somewhere in his papers on the history of the language, Stroustrup comments on this, but I can't find the exact reference right now.)

(*) String handling in B was interesting: you'd use arrays of int and pack multiple characters in each member. B was actually BCPL with different syntax.

Defining static constexpr auto class variable

The auto type-specifier serves two related but separate purposes

[dcl.spec.auto] / 1

The auto and decltype(auto) type-specifiers are used to
designate a placeholder type that will be replaced later by deduction
from an initializer. The auto type-specifier is also used to
introduce a function type having a trailing-return-type or to
signify that a lambda is a generic lambda.

In the case of your static member, the type is determined by the initializer, so x already has type int at the end of its declaration.

[dcl.spec.auto] / 4

The type of a variable declared using auto or decltype(auto) is
deduced from its initializer.

The rule you mention applies to functions and function templates only, and is unrelated to the use of auto when declaring variables.

[dcl.spec.auto] / 13

Redeclarations or specializations of a function or function template
with a declared return type that uses a placeholder type shall also
use that placeholder, not a deduced type.



Related Topics



Leave a reply



Submit