Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Why can't I return a shared pointer in C++14 when the function return type is bool?

I'm using g++ and write a simple function:

#include <memory>

std::shared_ptr<char> ptr;

bool fails_compiling()
{
    return ptr;
}

From what I can see in the interface, the shared_ptr implementation includes a bool operator and I can even apply a quick fix like this:

    return static_cast<bool>(ptr);

And it now compiles.

Why would the return algorithm not attempt an auto-conversion to bool like the if() and while() do?

like image 307
Alexis Wilke Avatar asked Sep 19 '16 00:09

Alexis Wilke


3 Answers

If you checkout std::shared_ptr's bool conversion operator, you will see that it's declared as:

explicit operator bool() const;

The use of explicit simply tells the compiler to forbid implicit conversion, which is what would have taken place because the return type of your function is different from the object type you are returning. However, this doesn't affect contextual conversions.

which occurs in the context of any:

  • controlling expression of if, while, for;
  • the logical operators !, && and ||;
  • the conditional operator ?:;
  • static_assert;
  • noexcept.

above quote cited from cppreference

like image 179
WhiZTiM Avatar answered Sep 20 '22 20:09

WhiZTiM


Why would the return algorithm not attempt an auto-conversion to bool like the if() and while() do?

std::shared_ptr::operator bool is explicit conversion function, so implicit conversion is not allowed, but static_cast (explicit conversion) works well.

When used for if or while, contextual conversions takes effect, then the explicit user-defined conversion function will be considered. In this case, std::shared_ptr is contextually convertible to bool.

In the following five contexts, the type bool is expected and the implicit conversion sequence is built if the declaration bool t(e); is well-formed. that is, the explicit user-defined conversion function such as explicit T::operator bool() const; is considered. Such expression e is said to be contextually convertible to bool.

  • controlling expression of if, while, for;
  • the logical operators !, && and ||;
  • the conditional operator ?:;
  • static_assert;
  • noexcept.
like image 25
songyuanyao Avatar answered Sep 23 '22 20:09

songyuanyao


Why the code doesn't compile.

std::shared_ptr's conversion-to-bool operator is declared explicit and so will generally not be invoked for an implicit conversion.

In particular it will not be invoked in the context of a return statement.

And it will not be considered for choosing a function overload, i.e. foo(p) will not resolve to an overload of foo that takes a bool argument.

There are however umpteen ways to express the conversion explicitly, including:

!!ptr

ptr != nullptr

ptr.get() != nullptr

static_cast<bool>( ptr )

ptr.operator bool()

The general case for implicit conversion to bool.

There are some cases where explicit on an operator bool() is ignored, in order to make things work mainly as in C++03. That is, to make things work as with the schemes employed before explicit was allowed on conversion operators in C++11. These exceptions are

  • use as a (grammar production) condition in an if, while or for, but not in a switch,

  • use as condition in :? choice, static_assert or noexcept,

  • use as argument to the built-in boolean operators &&, || and !, or their equivalents and, or and not.

Notably, again, these exceptions that allow implicit conversion to bool in spite of explicit, known as contextual conversions, do not include a return statement expression.


Can explicit be ignored also in other cases?

In what other cases, if any, can explicit on a conversion operator be ignored?

Well, none. But a non-explicit conversion operator, that is, an implicit conversion operator, can be invoked implicitly where the exact type to be converted to is not specified by the context.

C++11 §4/5 (conv):

Certain language constructs require conversion to a value having one of a specified set of types appropriate to the construct. An expression e of class type E appearing in such a context is said to be contextually implicitly converted to a specified type T and is well-formed if and only if e can be implicitly converted to a type T that is determined as follows: E is searched for conversion functions whose return type is cv T or reference to cv T such that T is allowed by the context. There shall be exactly one such T.

For example,

C++11 §5.3.5/1 (expr.delete):

If of class type, the operand [of delete] is contextually implicitly converted (Clause 4) to a pointer to object type.

… and so, a bit counter-intuitive to me!, the following should compile with a conforming compiler:

struct Foo
{
    operator int*() const { return nullptr; }
};

auto main()
    -> int
{
    delete Foo();
}
like image 43
Cheers and hth. - Alf Avatar answered Sep 19 '22 20:09

Cheers and hth. - Alf