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
boollike 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
boolis 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
eof class typeEappearing in such a context is said to be contextually implicitly converted to a specified typeTand is well-formed if and only ifecan be implicitly converted to a typeTthat is determined as follows:Eis searched for conversion functions whose return type is cvTor reference to cvTsuch thatTis 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