2

I tried to force explicit conversion in return statement by means of using of uniform initialization syntax as it is in following:

#include <iostream>

#include <cstdlib>

struct A
{

    explicit
    operator bool () const
    {
        return false;
    }

};

bool
f()
{
    return {A{}}; // error: no viable conversion from 'void' to 'bool'
    // equivalent to return bool{A{}}; at my mind
}

bool
g()
{
    return static_cast< bool >(A{});
}

struct B
{

    B(A) { ; }

};

B
h()
{
    return {A{}};
    // equivalent to return B{A{}};
}

inline
std::ostream &
operator << (std::ostream & _out, B const &)
{
    return _out << "B";
}

int
main()
{
    std::cout << std::boolalpha << f() << std::endl;
    std::cout << std::boolalpha << g() << std::endl;
    std::cout << h() << std::endl;
    return EXIT_SUCCESS;
}

But got an error, as it pointed against correspoinding line of code into comment.

Why deduced type of {A{}} is void? What is the meaning of return {/* something */}; in general? How it differs from obvious uniform initialization during simple initialization?

The compiler used is clang 3.6.0.

Tomilov Anatoliy
  • 15,657
  • 10
  • 64
  • 169
  • If you try `explicit B(A) { ; }`, it might help understand it. – R. Martinho Fernandes May 27 '15 at 10:12
  • Also possibly relevant: https://stackoverflow.com/questions/9157041/what-could-go-wrong-if-copy-list-initialization-allowed-explicit-constructors – R. Martinho Fernandes May 27 '15 at 10:14
  • @R.MartinhoFernandes WRT `explicit B(A) { ; }`: error is *error: chosen constructor is explicit in copy-initialization*. It sounds strange, considering `B b{A{}};` not raising any error. – Tomilov Anatoliy May 27 '15 at 10:26
  • @R.MartinhoFernandes OK, quote `In copy-list-initialization, if an explicit constructor is chosen, the initialization is ill-formed.` is somehow relevant to the question. But `bool` at my mind have no explicit constructors at all. – Tomilov Anatoliy May 27 '15 at 10:29
  • @Orient but your conversion function is `explicit` – Piotr Skotnicki May 27 '15 at 10:31
  • @PiotrS. Anyways it is *conversion-to*, but not *conversion-from* in some sense. – Tomilov Anatoliy May 27 '15 at 10:32
  • @Orient *"A conversion function may be explicit (7.1.2), in which case it is only considered as a user-defined conversion **for direct-initialization** (8.5).*" – Piotr Skotnicki May 27 '15 at 10:33
  • @PiotrS. Two above quotes gives the part of an answer. – Tomilov Anatoliy May 27 '15 at 10:35
  • 1
    @Orient as for the `void`-related issue, *braced init list* has no type at all, hence `void` – Piotr Skotnicki May 27 '15 at 10:37
  • @PiotrS. I'm under impression. Really it has no type? – Tomilov Anatoliy May 27 '15 at 10:39
  • @PiotrS. Even so, what is the trigger to consider it as initializer of `B` in another case? – Tomilov Anatoliy May 27 '15 at 10:42
  • @Orient special rules called *copy-list-initialization* / *direct-list-initialization*, or *list-initialization* in general – Piotr Skotnicki May 27 '15 at 10:50
  • @PiotrS. `void` doesn't mean "no type at all". `void` is a type; it means "no *value* at all". So, I don't think the fact that a *braced init list* is not an expression and has no type can be seen as a valid reason for `void` appearing in that error message. – bogdan May 27 '15 at 11:04
  • @PiotrS. OK, what about `bool f() { return {bool{}}; }`? – Tomilov Anatoliy May 27 '15 at 11:17
  • 3
    GCC 5.1.0 gives a better error message: `cannot convert '' to 'bool' in return`, which makes sense. MSVC14 RC gives an even better diagnostic: `'return': cannot convert from 'braced-init-list' to 'bool'` `note: Reason: cannot convert from 'A' to 'bool'` `note: No user-defined-conversion operator available that can perform this conversion, or the operator cannot be called`, which makes even more sense and precisely reflects what the standard specifies for this initialization. – bogdan May 27 '15 at 11:22
  • @Orient: `bool f() { return {bool{}}; }` is a direct-list-initialization with a braced-init-list containing a [value-initialized](http://en.cppreference.com/w/cpp/language/value_initialization) temporary bool variable. – davidhigh May 27 '15 at 11:33
  • 1
    @davidhigh It's not *direct-list-initialization*, it's *copy-list-initialization*. The initialization that occurs in function return is *copy-initialization*. – bogdan May 27 '15 at 11:41
  • @bogdan: in principle, yes. But in the context of copy-elision it seems to be direct-initilization: *When a nameless temporary, not bound to any references, would be moved or copied into an object of the same type (ignoring top-level cv-qualification), the copy/move is omitted. When that temporary is constructed, it is constructed directly in the storage where it would otherwise be moved or copied to.* from [here](http://en.cppreference.com/w/cpp/language/copy_elision) – davidhigh May 27 '15 at 12:22
  • @davidhigh your quote is irrelevant, even if copy-elision takes place the rules stay the same, copy-initialization remains copy-initialization – Piotr Skotnicki May 27 '15 at 12:26
  • @PiotrS, Bogdan. ok, thanks for correction. Should have better read point (8) [here](http://en.cppreference.com/w/cpp/language/list_initialization) :-) – davidhigh May 27 '15 at 12:28

1 Answers1

3

When you make the user-defined conversion explicit, you are forced to explicitly state what you want.

Remove the explicit and it works fine:

struct A
{
    operator bool () const
    {
        return false;
    }

    operator bool ()
    {
        return true;
    }
};

bool
f()
{
    return A{};   // conversion from A to bool
    return {A{}}; // conversion from braced-init list to bool 
}

EDIT: I'll try to work out your reasonable questions:

With bool{A{}}; you apply direct-list-initialization and then return the result (which in the general case implies a move, a copy or RVO). But as there is no implicit conversion from A{} to bool, it fails. Why? Because an explicit conversion operator only participates in direct-initialization or explicit casts, see point (2) here.

The second case where you return {A{}} is similar, but you first construct the init-list and then pass it to copy-list-initialize the bool return value. So the same problem as in bool{A{}}; occurrs.

EDIT: for the reason why there is a void involved, see the comments by @PiotrS.

davidhigh
  • 14,652
  • 2
  • 44
  • 75