What is the type of variable "something" in C++ 11 standard initialized by
auto something = nullptr;
?
The type of that is std::nullptr_t which is a single valued type introduced in C++11 to represent a null pointer and which is implicitly convertible to any other pointer type.
Related
I read now unique_ptr source code in libstdc++.
public:
typedef _Tp* pointer;
typedef _Tp element_type;
typedef _Tp_Deleter deleter_type;
// Constructors.
unique_ptr()
: _M_t(pointer(), deleter_type())
{ static_assert(!std::is_pointer<deleter_type>::value,
"constructed with null function pointer deleter"); }
I don´t understand.Does "pointer()" call constructor? but "pointer" is an alias for type _Tp*
For all types, T() is an expression that value-initialises an unnamed instance of that type. For non-class, non-array types, value-initialisation is zero-initialisation
the object's initial value is the integral constant zero explicitly converted to T
And for a pointer type, that's a null pointer
This expression
pointer()
zero-initializes the data member pointer of the class that has a pointer type. For pointer types it means setting a pointer to a null pointer.
From the C++ 14 Standard (8.5 Initializers)
11 An object whose initializer is an empty set of parentheses, i.e.,
(), shall be value-initialized
and
8 To value-initialize an object of type T means:
(8.4) — otherwise, the object is zero-initialized.
Further
6 To zero-initialize an object or reference of type T means:
(6.1) — if T is a scalar type (3.9), the object is initialized to the
value obtained by converting the integer literal 0 (zero) to T;104
and in the footnote 104 there is written
As specified in 4.10, converting an integer literal whose value
is 0 to a pointer type results in a null pointer value
This definition works:
const auto &b{nullptr};
while this fails:
auto *b{nullptr};
I have tried to compile this in Visual C++, GCC, and Clang. They all complain "cannot deduce type".
In the second case, shouldn't b be deduced to have some type like std::nullptr_t?
It's because you declare b to be a pointer, and initialize it to be a null pointer. But a null pointer to what type of data you don't say, so the compiler can't deduce the type.
If you want b to be a std::nullptr_t object, you should drop the asterisk:
auto b{nullptr};
decltype(nullptr) is std::nullptr_t.
so with
const auto &b{nullptr}; // auto is std::nullptr_t
// b is a reference to a temporary (with lifetime extension)
but nullptr is NOT a pointer (even if it is convertible to).
so auto *b{nullptr}; is invalid.
You might use instead
auto b{nullptr}; // auto is std::nullptr_t
nullptr is of type std::nullptr_t. As a nullptr does not point to anything, there is no corresponding pointee type for std::nullptr_t (you are not allowed to dereference a nullptr), hence
auto *b { nullptr};
requests a type that does not exist. If you want b to be of type nullptr_t simply write
auto b { nullptr};
While looking at the implementation of nullptr here, what got my attention is that nullptr is rvalue which means we can do something like this
std::nullptr_t&& nullref = nullptr;
But how could nullptr be rvalue since the implementations is something like this
const class {...} nullptr = {};
Is this core feature ? What am I missing ?
Implementation has nothing to do with it.
The keyword nullptr is defined to produce an rvalue expression, and that's the end of it.
[C++14: 2.14.7/1]: The pointer literal is the keyword nullptr. It is a prvalue of type std::nullptr_t. [ Note: std::nullptr_t is a distinct type that is neither a pointer type nor a pointer to member type; rather, a prvalue of this type is a null pointer constant and can be converted to a null pointer value or null member pointer value. See 4.10 and 4.11. —end note ]
I agree you couldn't yourself reimplement it to this criterion in userspace, but then that's the case for every single other keyword also.
Expressions consisting only of the keywords true and false are also rvalues, if you're curious.
[C++14: 2.14.6/1]: The Boolean literals are the keywords false and true. Such literals are prvalues and have type bool.
If Alternative #1 were the definition of nullptr, then you are right that it would be an lvalue. However, it could be forced to be an rvalue using something like this:
const class __nullptr_t {...} __nullptr = {};
#define nullptr (__nullptr_t(__nullptr));
That's not what was ultimately standardized though. In actual C++11, nullptr is a literal, the same way as 3.14 or 'x'.
Consider the code below:
#include <iostream>
#include <memory>
void f(std::shared_ptr<int> sp) {}
template <typename FuncType, typename PtrType>
auto call_f(FuncType f, PtrType p) -> decltype(f(p))
{
return f(p);
}
int main()
{
f(0); // doesn't work for any other int != 0, thanks #Rupesh
// call_f(f, 0); // error, cannot convert int to shared_ptr
}
In the first line in main(), the integer 0 is converted to a std::shared_ptr<int> and the call f(0) succeeds without any problem. However, using a template to call the function make things different. Second line will not compile anymore, the error being
error: could not convert 'p' from 'int' to 'std::shared_ptr<int>'
My questions are:
Why does the first call succeed and the second doesn't? Is there anything I'm missing here?
I don't understand also how the conversion from int to std::shared_ptr is being performed in the call f(0), as it looks like std::shared_ptr has only explicit constructors.
PS: A variant of this example appears in Scott Meyers' Effective Modern C++ Item 8, as a way of protecting such calls with nullptr.
std::shared_ptr has a constructor that takes std::nullptr_t, literal 0 is a null pointer constant that is convertiable to std::nullptr_t from the draft C++ standard section 4.10 [conv.ptr] (emphasis mine going forward):
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 can be converted to a
pointer type; the result is the null pointer value of that type and is
distinguishable from every other value of object pointer or function
pointer type. Such a conversion is called a null pointer conversion.
Two null pointer values of the same type shall compare equal. The
conversion of a null pointer constant to a pointer to cv-qualified
type is a single conversion, and not the sequence of a pointer
conversion followed by a qualification conversion (4.4). A null
pointer constant of integral type can be converted to a prvalue of
type std::nullptr_t. [ Note: The resulting prvalue is not a null
pointer value. —end note ]
in your second case p is being deduced as type int which although has the value zero is no longer a null pointer constant and so does not fit the same case.
As T.C. points out the wording was changed with DR 903 which requires an integer literal with value zero as opposed to an integral constant expression which evaluates to zero:
A null pointer constant is an integer literal (2.14.2) with value
zero or a prvalue of type std::nullptr_t. A null pointer constant
can be converted to a pointer type; the result is the null pointer
value of that type and is distinguishable from every other value of
object pointer or function pointer type.
Per [conv.ptr]/1 (quoting N4296 here):
A null pointer constant is an integer literal (2.13.2) with value 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.
shared_ptr has a non-explicit constructor that accepts std::nullptr_t per [util.smartptr.shared.const]/1:
constexpr shared_ptr(nullptr_t) noexcept : shared_ptr() { }
which constructs an empty, non-owning shared_ptr.
When you call f(0) directly, 0 is a null pointer constant that is implicitly converted to shared_ptr<int> by the above constructor. When you instead call call_f(f, 0), the type of the literal 0 is deduced to int and of course an int cannot be converted to a shared_ptr<int>.
The firs call f(0) is compiled as f(nullptr), which is fine for the compiler (but it should not be in my opinion). The second call will create declaration for a function to work on any int, which is illegal.
Funny thing is, that even this code works:
f(3-3);
f(3*0);
Now that C++0x is almost here, I've been experimenting with it, and in particular using nullptr. I haven't been able to figure out what standard header files one is supposed to include if one needs to use it.
Any help is appreciated.
No headers should be required. It is a built-in keyword (§[lex.nullptr]).
2.14.7 Pointer literals [lex.nullptr]
pointer-literal:
nullptr
The pointer literal is the keyword
nullptr. It is a prvalue of type
std::nullptr_t. [ Note: std::nullptr_t
is a distinct type that is neither a
pointer type nor a pointer to member
type; rather, a prvalue of this type
is a null pointer constant and can be
converted to a null pointer value or
null member pointer value. See 4.10
and 4.11. —endnote]
Its type, std::nullptr_t, however, is "defined" in the header <cstddef> (§[support.types]/9).
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. [Note: Although nullptr’s address cannot be taken, the address of another nullptr_t object that is an lvalue can be taken. —endnote]