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?
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
Why would the return algorithm not attempt an auto-conversion to
bool
like theif()
andwhile()
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 declarationbool t(e);
is well-formed. that is, the explicit user-defined conversion function such asexplicit 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.
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()
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.
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.
” 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 typeE
appearing in such a context is said to be contextually implicitly converted to a specified typeT
and is well-formed if and only ife
can be implicitly converted to a typeT
that is determined as follows:E
is searched for conversion functions whose return type is cvT
or reference to cvT
such thatT
is allowed by the context. There shall be exactly one suchT
.
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();
}
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With