16

I can't tell from the C++11 Standard if nullptr_t has a default constructor. In other words, is the following valid?:

nullptr_t n; 

GCC and VC++ allow the above code, but clang does not. I can't find anything in the Standard specifying that it doesn't have a default constructor, and what I can find suggests that it ought to. This matters to me because I'm writing a basic fallback implementation of nullptr for older compiler support and need to know if I need to give it a default constructor.

ComicSansMS
  • 51,484
  • 14
  • 155
  • 166
ThreeBit
  • 608
  • 6
  • 17
  • I thought you were supposed to handle a `nullptr_t` as a plain pointer type, i.e. not as a class. So I'm assuming `nullptr_t n;` creates an _uninitialised_ variable; you're supposed do write `nullptr_t n = nullptr;` explicitly. But I don't have a C++11 compiler here, so I can't check. And I can't seem to find where I read it in the formal specs... – Mr Lister Mar 31 '12 at 07:28
  • FWIW, clang accepts "nullptr_t n;" here. – Johannes Schaub - litb Mar 31 '12 at 15:20

2 Answers2

17

What the Standard says

The standard says (18.2)

nullptr_t is defined as follows:

namespace std {
   typedef decltype(nullptr) nullptr_t;
}

The type for which nullptr_t is a synonym has the characteristics described in 3.9.1 and 4.10.

Where 3.9.1 basically says it should be of the same size as void* and 4.10 specifies the conversion rules for nullptr.

Edit: 3.9.9 furthermore explicitly states that nullptr_t is a scalar type, which means the expected initialization rules for built-in types from 8.5 apply:

  • Default-initialization (nullptr_t n;), which leaves the value of n undefined. As Johannes Schaub pointed out correctly, this compiles fine with the newest version of Clang.
  • Value-initialization (nullptr_t n = nullptr_t();), which initializes n to 0.

This behavior is identical to e.g. int, so nullptr_t is definitely default-constructible. The interesting question here is: What does it mean for nullptr_t to have undefined value? At the end of the day, there is only one meaningful possible value for nullptr_t, which is nullptr. Furthermore the type itself is only defined through the semantics of the nullptrliteral. Do these semantics still apply for an unitialized value?

Why that question doesn't matter in practice

You don't want to declare a new variable of type nullptr_t. The only meaningful semantic of that type is already expressed through the nullptr literal, so whenever you would use your custom variable of type nullptr_t, you can just as well use nullptr.

What does matter in practice

The only exception to this comes from the fact that you can take non-type template parameters of type nullptr_t. For this case, it is useful to know which values can convert to nullptr_t, which is described in 4.10:

A null pointer constant is an integral constant expression (5.19) prvalue of integer type that evaluates to zero or a prvalue of type std::nullptr_t. [...] A null pointer constant of integral type can be converted to a prvalue of type std::nullptr_t.

Which basically does just what you'd expect: You can write

nullptr_t n = 0;    // correct: 0 is special

but not

nullptr_t n = 42;   // WRONG can't convert int to nullptr_t

Both gcc 4.6 and Clang SVN get this right.

ComicSansMS
  • 51,484
  • 14
  • 155
  • 166
  • 1
    You are making the mistake of generalizing something that is true of classes to non-classes. Non-classes do not have any member functions at all. – Jirka Hanika Mar 31 '12 at 09:01
  • 1
    Non-class types can be default-constructible without having a member function. This is not about having a default constructor in the sense of having a member function, but in the sense of fulfilling the concept of a default-constructible type. – ComicSansMS Mar 31 '12 at 09:10
  • From the standard: "The type for which nullptr_t is a synonym has the characteristics described in 3.9.1 and 4.10." Visit section 3.9.1 (Fundamental types) and learn that you can declare int-typed, float-typed, indeed, nullptr_t-typed variables. – Jirka Hanika Mar 31 '12 at 14:22
  • If you say "has a default constructor" that definitely *is* about "having a default constructor in the sense of having a member function", because a default constructor is a member function. If you want to say something else, you cannot say "has a default constructor". – Johannes Schaub - litb Mar 31 '12 at 14:50
  • You guys made me question my original answer and take a closer look at the problem. While I still couldn't find a guarantee for `nullptr_t` to be default-constructible, I am now convinced that it *should* be. I edited my answer above accordingly. Thanks for the critical feedback. – ComicSansMS Mar 31 '12 at 16:59
  • You made a big step forward, but I still don't this answer on the right track with the DefaultConstructible concept. `int`, for example, does not have a default constructor, and concepts (new in C++11) have nothing to do with that. Or at least nothing that I'd currently see mentioned anywhere in the C++11 standard, or in this question. The question clearly talks about a default constructor, not about the new concepts. – Jirka Hanika Mar 31 '12 at 19:58
  • 1
    @Jirka Hanika: `int` *is* a model for DefaultConstructible. As required by 17.6.3.1 it allows both default-initialization (`int i;` - `i`'s value is undefined) and value-initialization (`int i = int();` - `i` is set to 0). This has been true for C++98 just as well as for C++11. [Boost](http://www.boost.org/doc/libs/1_49_0/doc/html/DefaultConstructible.html) even mentions it as an example. – ComicSansMS Mar 31 '12 at 22:51
  • @ComicSansMS - Automatic variables are not default-initialized. Where did you read they were? – Jirka Hanika Mar 31 '12 at 23:28
  • @Jirka Hanika: They are when using the value initialization syntax. It's one of the lesser known C++ features. See [this thread](http://stackoverflow.com/questions/5113365/do-built-in-types-have-default-constructors). Actually, this might just be what's needed to answer this question... (Hopefully final) edit inc... – ComicSansMS Apr 01 '12 at 06:57
  • 2
    @ComicSansMS - Good progress again. Thank you. They are value-initialized when using value initialization syntax but I think I get what you are trying to say. Anyway, there is one other point where the answer is still misleading. The uninitialized value definitely does not have to be `nullptr`. An uninitialized `bool` may contain `true`, `false`, or also any other bit pattern, and computations using such "third values" are undefined and may lead to logically "impossible" branch paths. I suspect but have not checked that it is the same with this new unnamed type (compiler might warn). – Jirka Hanika Apr 01 '12 at 09:29
  • @Jirka Hannika I completely agree with what you said on uninitialized values. However, I'm not convinced that something like `nullptr_t n; if(n) {...}` does necessary yield undefined behavior (although it probably should). The way I interpret the current wording, we have contradictory statements on what happens when accessing uninitialized values vs. what happens when converting values of `nullptr_t` to `bool`. This is however, a purely academic question. Anyone who actually uses a construct like that certainly does deserve to be cast into the abyss of undef behavior ;) – ComicSansMS Apr 01 '12 at 10:51
  • 1
    @JirkaHanika in C++11 all variables that do not have an explicit initializer are default initialized. That was a sensible change they did. In C++03 it only applied for non-PODs, which was a bit weird because it was just another special rule. – Johannes Schaub - litb Apr 01 '12 at 12:29
  • @ComicSansMS: Nice answer. My only issue is the part about never declaring a new symbol of type `nullptr_t`. In some cases, an alias could be very useful for readability (e.g., `constexpr nullptr_t END_NODE = nullptr` for some kind of container implementation). – etherice Aug 12 '13 at 09:00
  • @etherice For constant symbols that is certainly true. But as with all constants, you would not want to leave those uninitialized in the first place. – ComicSansMS Aug 12 '13 at 09:26
  • @JohannesSchaub-litb: A default-initialized POD will still have an *indeterminate* value, so what difference does it make whether the rule of `variables that do not have an explicit initializer are default initialized` applies to POD's or not? – etherice Aug 12 '13 at 09:38
  • @JirkaHanika: The "default initialization leaves the variable undefined" (that is, *indeterminate*) is actually true for scalar types like `nullptr_t`. – etherice Aug 12 '13 at 09:47
  • @etherice - Are you using all the terminology correctly? For me, `int x;` or `int x = new int;` is indeterminate in C++03, in contrast with default initialization such as `int x = new int();`, only the latter guaranteed to end up with a value of zero. – Jirka Hanika Aug 12 '13 at 11:00
  • 2
    @etherice i dont think it makes a difference to the user, but i think it makes it more easy to grasp the spec (personal opinion ofc). – Johannes Schaub - litb Aug 12 '13 at 17:35
  • @JirkaHanika: Positive. And I'm not sure why you're discussing C++03 when this is clearly C++11 (given that we're discussing scalar type `nullptr_t`). As already pointed out by Johannes, any POD not explicitly initialized is `default-initialized` in C++11. And as he and I both clarified, this makes no difference to the programmer since `default-initialization` for POD types is *nothing*, leaving the variable undefined/indeterminate. This is true for C++11 **and** earlier standards; the only difference is whether it's *considered* `default-initialized` in terms of the C++ standard in question. – etherice Aug 13 '13 at 18:57
  • 2
    @JirkaHanika: One more thing: You're not using correct terminology when you say "... default initialization such as int x = new int(); ...". `int()` is `value-initialization`, not `default-initialization`. Per C++11 § 8.5.10: `An object whose initializer is an empty set of parentheses, i.e., (), shall be value-initialized.` Per 8.5.7 (summarized): `value-initialization for POD types is zero-initialization`. Per 8.5.11: `If no initializer is specified for an object, the object is default-initialized.` Per 8.5.6 (summarized): `default-initialization for POD types is to do nothing at all.`. – etherice Aug 13 '13 at 19:11
  • @etherice - I was just misunderstanding an earlier comment here and thus also where you were heading. All clear now (thanks to you and @JohannessSchaub-litb) and my two misguided comments deleted. – Jirka Hanika Aug 14 '13 at 12:09
1

While nullptr is a new addition to the language itself, std::nullptr_t is just an alias of an unnamed type, the alias declared in cstddef like this:

typedef decltype(nullptr) nullptr_t;

While nullptr_t, being a typedef and not a language keyword, is not listed as a fundamental type, it is specified to behave as a fundamental type (and not, for example, as a pointer type or a class type). Therefore it does not have a default constructor, but you can still declare a variable like you have done. Your variable is not initialized and I wonder what its use could be and what error message you exactly got from clang.

See also here.

Community
  • 1
  • 1
Jirka Hanika
  • 13,301
  • 3
  • 46
  • 75
  • I think the spec talking about values of nullptr_t in the "Fundamental types" section implies that nullptr_t is a fundamental type. There is no explicit list of what types are fundamental types, which means one has to gather them by iterating over all types mentioned in "Fundamental types". And it makes a lot of sense that it is a fundamental type anyway. – Johannes Schaub - litb Mar 31 '12 at 15:08
  • 4
    Note that you cannot say "Type nullptr_t is a library typedef". A typedef does *not* introduce a new type. It simply creates an alias to an existing type. The type that "nullptr_t" aliases *is* introduced by the language. – Johannes Schaub - litb Mar 31 '12 at 15:12
  • @JohannesSchaub-litb - OK, edited the question to make this point more clear. This point is, the type of `nullptr` has no name or alias in a C++ program until an alias is assigned (best done using the standard library header file). – Jirka Hanika Mar 31 '12 at 18:57