62

Does C++11 allow declaring non-static data members as 'auto' if they are initialized in the declaration? For example:

struct S
{
    auto x = 5;  // in place of 'int x = 5;', which is definitely allowed
};

GCC 4.7 rejects the above code, while it accepts int x = 5;.

Assuming this is not a compiler bug but rather the standard really doesn't allow it, why not? It would be just as useful as declaring local variables auto.

HighCommander4
  • 50,428
  • 24
  • 122
  • 194
  • 6
    It is allowed to declare a static data member this way, but not a non-static data member (see the list of allowed `auto` uses in C++11 §7.1.6.4). Off the top of my head, I can't think of a good reason why it is prohibited, though I would definitely consider this to be an abuse of `auto`. – James McNellis Jul 03 '12 at 00:32
  • 1
    @JamesMcNellis: Why would you consider this an abuse of `auto`? What is the difference between having a variable with a long/complicated type name (so that it is desirable to use `auto` rather than write out its type) at class scope vs. having one at local scope? – HighCommander4 Jul 03 '12 at 00:34
  • 5
    At block scope I know how the variable is being _used_. At class scope or namespace scope, I do not necessarily know how the variable is being used. Consider, for example, the more general `auto x = f(a, b, c);`: I'd have to go find all of the `f()` functions and perform overload resolution in my head to figure out the type of `x`. At least at local scope I can look to see what is being done with `x` and try to deduce from that what its type is. `auto` is insanely useful, but it shouldn't be used _everywhere_. – James McNellis Jul 03 '12 at 00:36
  • 2
    @JamesMcNellis: Interestingly, `auto` *is* allowed at namespace scope. I don't see why class scope is singled out like that... – HighCommander4 Jul 03 '12 at 00:38
  • @JamesMcNellis: I agree in general, though this use doesn't fall under your definition of abuse as `x` is initialized with an `int` literal. – Ed S. Jul 03 '12 at 00:45
  • @EdS.: Yes, my example was intended to demonstrate why I would be concerned by use of `auto` with arbitrary initializers at class or namespace scope. I think a lot of people are going way too far with use of `auto`. It's very useful, but not for everything. In my code base, I use it for loop iterators, unutterable types, and very occasionally for local variables. Type names are very useful for improving the comprehensibility of code in many circumstances. – James McNellis Jul 03 '12 at 01:50
  • @JamesMcNellis: I agree, the same thing happened to a lot of C# projects. All you see is `var` this, `var` that, and `var ugly = SomeFunc(someArg).DoIt().OneMoreTime()`. – Ed S. Jul 03 '12 at 03:12
  • 4
    See [this ISOCPP mailing list discussion](https://groups.google.com/a/isocpp.org/forum/?fromgroups=&pli=1#!searchin/std-proposals/auto%2420member/std-proposals/MDyiiVR8J-s/w_hzTl056FcJ) about a prototype implementation of non-static auto members, and also the committee's discussion thereof recorded in [N3897 Auto-type members](http://www.open-std.org/jtc1/sc22/wg21/docs/papers/2014/n3897.html). – Casey May 27 '14 at 16:54
  • Digging up an old post here but using `auto` in a class is used in _The C++ Programming Language, Fourth Edition_ which covers C++11. Mildly annoying that it is not actually C++11. (Found while copying an example on page 1182). – pipe Sep 19 '18 at 20:59

3 Answers3

75

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.

Jesse Good
  • 50,901
  • 14
  • 124
  • 166
  • 9
    Thanks for digging that up, that's an interesting rationale! – HighCommander4 Jul 03 '12 at 01:10
  • 2
    N.B. the original proposal to allow it was [N2426](http://www.open-std.org/jtc1/sc22/wg21/docs/papers/2007/n2426.htm) – Jonathan Wakely Mar 06 '13 at 00:14
  • 1
    @JonathanWakely There's really no hope to see that topic resurrected say for C++20? There are several places in my code that would be much more readable if it were allowed. Mostly `auto foo_ = Type{...};`, so just that case would already help. – akim Dec 23 '16 at 15:01
  • 9
    Wouldn't that rationale also apply to `decltype(func()) data = func()`, which is legal now? – krzaq Jun 13 '17 at 11:05
  • 2
    I don't see why `static` makes a difference though. What am I missing? – Tim Randall Jul 23 '21 at 13:45
  • What a shame, it makes even capable people invent silly formatting like the following: [see how class `layered_button` uses tabulation to line up the defined names](https://www.cycfi.com/2019/07/photon-micro-gui/). Names defined just need to go on the left. -1 for the National Body. – Patrick Fromberg Feb 12 '22 at 16:32
  • 1
    @TimRandall That's because static data members do no impact the layout of a class. – lrineau Jan 08 '23 at 18:35
6

For others:

Using C++17 this is indirectly (automatic deduction of non-static member type) possible. You need to use templates and deduction guides to make it happen:

template< class data_t>
struct MyType 
{
    data_t data;
    static constexpr auto data_size = sizeof(data_t);

    MyType( data_t && p_data ) : data(p_data) {}
};

template< class data_t>
MyType(data_t &&) -> MyType<std::remove_reference_t<data_t>>;

I don't know how but this auto members really need to make it into the language without them certain patterns are next to impossible.

The scenario above does not work if the lambda captures a member of class by reference. This is a useful pattern for, highly compensable classes that avoid the use of type erased functions. Something I regularly do on embedded systems.

https://godbolt.org/z/W-K9Uk

You can mangle the language into submission allowing a lambda to reference a member of a non-existent class using placement-new and offset_of but that is ludicrous and shouldn't be required.

David Ledger
  • 2,033
  • 1
  • 12
  • 27
  • 6
    I don't understand how this is an answer. The class does not have a data member declared as 'auto'. – HighCommander4 Sep 24 '18 at 18:49
  • 1
    You get automatic member type deduction. Which appears to be the end goal of the question. This is how I got around the need for auto as a member of classes in a few applications. It is a real work around. As stated at the top, For Others who are searching for that. – David Ledger Sep 25 '18 at 05:13
  • Perhaps a larger example, that illustrates how this might be used, would be helpful, because I don't see it just yet. – HighCommander4 Sep 25 '18 at 16:37
  • Sure, take this implementation of a lambda wrapper. The type of the lambda is unknown, it MUST be deduced. https://wandbox.org/permlink/wCzrLsPc9k0YErDA Remember this isn't C++11, this is just for others who have a similar question and for your curiosity I suppose. – David Ledger Oct 18 '18 at 03:35
  • Ok, thanks, I understand the idea now. It doesn't seem like a general solution, though. In the general case, when all you want to do is deduce the type of a specific data member from its initializer, you're not going to template the whole class on the type of the data members and change how it's constructed, to accomplish that. – HighCommander4 Oct 18 '18 at 04:48
  • Yeah, that definable isn't standard but I've ended up doing that a lot in embedded systems. Particularly to avoid std::function and associated overhead. If you have some sensible default arguments it might be invisible. I really really want auto for members, it would make all this much cleaner. Auto for a member would result in an completely invisible template parameter for that member. – David Ledger Oct 18 '18 at 04:50
  • 1
    I understand and agree. Consider this: you just sprinkle your ideas and make them work almost all inside a main() ... auto's and lambdas and the lot. You make it all work. Then you need to deliver this as a class. Since you have been using lambdas and auto's not an easy task. On the edge of *not feasible*. What can you do? You usually backtrack from lambdas back to the `std::function<>` since you can not have auto type in a class .. I hope I am making myself clear? I just spent few hours trying to do exactly that, and then I found this answer. +1 – Chef Gladiator Mar 02 '19 at 17:12
  • 1
    How does your code help to declare a non-static data member? I get an `error: use of class template 'MyType' requires template arguments; argument deduction not allowed in non-static struct member`, see also here: https://godbolt.org/z/jxhcPGre4 – eel76 Jul 22 '21 at 09:31
  • Yeah, you'd need to deduce that too, like this: https://godbolt.org/z/vaq6j6nTT You'd need to do it the whole way down. Its not great, but its what we have... – David Ledger Jul 22 '21 at 12:18
1

Later discussion was publicly summarized in 2014: the critical point was that a default member initializer is a complete-class context but the type of any member is not. One further bit of rationale is that it would be odd to have

std::vector<int> get_ints();
struct S {
  auto x=get_ints();
  S() : x(7,4) {}
};

use the type of the default member initializer despite completely ignoring it otherwise.

Davis Herring
  • 36,443
  • 4
  • 48
  • 76