Implicit conversion from int to shared_ptr

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 } 

On the first line in main() integer 0 converted to std::shared_ptr<int> , and f(0) is called without any problems. However, using a template to call a function does different things. The second line will no longer compile, error

 error: could not convert 'p' from 'int' to 'std::shared_ptr<int>' 

My questions:

  • Why is the first call successful and the second not? Is something missing here?
  • I also do not understand how the conversion from int to std::shared_ptr is done in the call to f(0) , because it looks like this: std::shared_ptr has only explicit constructors.

PS: A variation of this example appears in Scott Meyers' Effective Modern C ++ 8, as a way to protect such calls with nullptr .

+6
source share
3 answers

std :: shared_ptr has a constructor that takes std :: nullptr_t, literal 0 is a null pointer constant that can be converted to std :: nullptr_t from a draft of a standard C ++ 4.10 section [conv.ptr] (highlighting my move):

The null pointer constant is the integer constant expression (5.19) prvalue of an integer type that evaluates to zero or a prvalue of type std :: nullptr_t . The null pointer constant can be converted to a pointer type; the result is a null pointer value of this type and different from any other pointer value or object function pointer type. This conversion is called null pointer conversion. Two null pointer values โ€‹โ€‹of the same type are compared equal. the conversion of a null pointer constant to a pointer to a cv-qualification type is one conversion, not a sequence of pointer conversion followed by qualification conversion (4.4). The null constant value pointer of an integral type can be converted to prvalue type std :: nullptr_t . [Note: the resulting prvalue is not a null pointer value. -end note]

in your second case, p is inferred as an int type, which, although it has a value of 0, is no longer a constant of a null pointer and therefore is not suitable for the same case.

As TC indicates that the wording was changed using DR 903 , which requires an integer literal with a value of 0 as opposed to an integral constant expression that is zero:

The null pointer constant is an integer literal (2.14.2) with a value of zero or prvalue of type std :: nullptr_t. The null pointer constant can be converted to a pointer type; the result is a null pointer value of this type and is different from any other value object pointer or function pointer type.

+5
source

Per [conv.ptr] / 1 (here N4296):

The null pointer constant is an integer literal (2.13.2) with a value of 0 or the value of the class std::nullptr_t .... The constant of a null pointer of an integral type can be converted to a prvalue of type std::nullptr_t .

shared_ptr has an implicit constructor that takes std::nullptr_t per [util.smartptr.shared.const] / 1:

 constexpr shared_ptr(nullptr_t) noexcept : shared_ptr() { } 

which creates an empty, not 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 constructor above. When you call call_f(f, 0) , the literal type 0 is call_f(f, 0) to int , and of course, int cannot be converted to shared_ptr<int> .

+2
source

The first call to f (0) compiles as f (nullptr), which is great for the compiler (but it shouldn't be in my opinion). The second call will create an declaration for the function to work with any int, which is illegal.

It's funny that even this code works:

 f(3-3); f(3*0); 
+1
source

Source: https://habr.com/ru/post/982693/


All Articles