Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Why do switch and if statements behave differently with conversion operators?

Why do switch and if statements behave differently with conversion operators?

struct WrapperA
{
    explicit operator bool() { return false; }    
};

struct WrapperB
{
    explicit operator int() { return 0; }
};

int main()
{
    WrapperA wrapper_a;
    if (wrapper_a) { /** this line compiles **/ }

    WrapperB wrapper_b;
    switch (wrapper_b) { /** this line does NOT compile **/ }
}

The compilation error is switch quantity is not an integer while in the if statement it is perfectly recognized as a bool. (GCC)

like image 254
nyarlathotep108 Avatar asked Jun 06 '18 12:06

nyarlathotep108


People also ask

When should you use a switch case instead of ELSE IF statements?

Main Difference between If-else and Switch Case The main differences between the two are: The if-else statement is used to choose between two options, but the switch case statement is used to choose between numerous options. If the condition inside the if block is false, the statement inside the else block is executed.

Can you convert a switch statement to an equivalent if statement or vice versa?

You can always convert a switch statement to an equivalent if statement. You can always convert an if statement to a switch statement. The break keyword must be used in a switch statement; otherwise, a syntax error occurs.

Can you put an if statement in a switch statement?

Sure can, see the example below (this is proof of concept and not tested). Note that you can also use a switch inside a switch, just like nested if statements.

Is switch case a conditional statement?

switch is a type of conditional statement that will evaluate an expression against multiple possible cases and execute one or more blocks of code based on matching cases.


2 Answers

The syntax is switch ( condition ) statement with

condition - any expression of integral or enumeration type, or of a class type contextually implicitly convertible to an integral or enumeration type, or a declaration of a single non-array variable of such type with a brace-or-equals initializer.

Taken from cppreference.

This means you can only do a switch case on an integer or enum type. For the compiler to be able implicitly convert Wrapper to integer / enum type you need to remove the explicit keyword :

The explicit specifier specifies that a constructor or conversion function (since C++11) doesn't allow implicit conversions

You can also cast Wrapper to int type.

Edit to adress @acraig5075 remarks :

You must be careful which operator is explicit and which is implicit. If both are implicit the code won't compile because there will be an amibiguity :

struct Wrapper
{
    operator int() { return 0; }
    operator bool() { return true; }    
};

source_file.cpp: In function ‘int main()’: source_file.cpp:12:14:

error: ambiguous default type conversion from ‘Wrapper’

switch (w) {

^ source_file.cpp:12:14: note: candidate conversion

include ‘Wrapper::operator int()’ and ‘Wrapper::operator bool()’

The only way to remove the ambiguity is to do a cast.

If only one of the operator is explicit, the other one will be chosen for the switch statement :

#include <iostream>
struct Wrapper
{
    explicit operator int() { return 0; }
    operator bool() { return true; }    
};

int main()
{
    Wrapper w;
    if (w) { /** this line compiles **/std::cout << " if is true " << std::endl; }
    switch (w) { 
        case 0:
            std::cout << "case 0" << std::endl;
            break;
        case 1:
            std::cout << "case 1" << std::endl;
            break;
    }
    return 0;
}

Output :

 if is true 
case 1

w has been implicitly converted to 1 (true) (because operator int is explicit) and case 1 is executed.

On the other hand :

struct Wrapper
{
    operator int() { return 0; }
    explicit operator bool() { return true; }    
};

Ouput :

 if is true 
case 0

w has been implicitly converted to 0 because operator bool is explicit.

In both case, the if statement is true because w is evaluated contextually to a boolean inside the if-statement.

like image 200
Clonk Avatar answered Oct 06 '22 08:10

Clonk


I think this explains why the switch statement is not accepted, whereas the if statement is:

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 40
Marco Luzzara Avatar answered Oct 06 '22 10:10

Marco Luzzara