Search code examples
c++conditional-operator

Why the result of conditional / ternary operator may differ from if-else statements in some c++ types?


Here is my code:

// defs
string untrusted_ip;
const char * get_env (const char *name, const char *envp[]);
string User::getCommonname(void);
void User::setStatusFileKey(string);
void User::setKey(string);

// 1
                if( get_env ( "untrusted_ip", envp ) !=NULL ){
                    newuser->setStatusFileKey(newuser->getCommonname() +string ( "," ) + untrusted_ip + string ( ":" ) + get_env ( "untrusted_port", envp ) );
                    newuser->setKey(untrusted_ip + string ( ":" ) + get_env ( "untrusted_port", envp ) );
                }else{
                    newuser->setStatusFileKey(newuser->getCommonname() +string ( "," ) + untrusted_ip);
                    newuser->setKey(untrusted_ip);
                }
                
// 2
                newuser->setStatusFileKey(newuser->getCommonname() +string ( "," ) + untrusted_ip + get_env ( "untrusted_ip", envp ) != (const char*)NULL ? string ( ":" ) + get_env ( "untrusted_port", envp ) : string("") );
                newuser->setKey(untrusted_ip + get_env ( "untrusted_ip", envp ) != (const char*)NULL ? string ( ":" ) + get_env ( "untrusted_port", envp ) : string("") );

modified from https://salsa.debian.org/debian/openvpn-auth-radius/-/blob/master/radiusplugin.cpp#L446

block 1 and 2 seems to be equal but 1 works as expected while 2 does not work (seems not executing, for break point is not triggered).

What is the core difference between the two blocks of codes?

Also, only get_env ( "untrusted_ip", envp ) != (const char*)NULL in conditional operator can pass compilation while if( get_env ( "untrusted_ip", envp ) !=NULL ) is possible. What is the reason and are the two problems connected?

P.S. I am using gcc/g++ 10.2.1


Solution

  • It's an operator precedence issue.

    The additions operator + have higher precedence than != so the expression is equivalent to (slightly rewritten to make it easier to see):

    auto result = newuser->getCommonname() +string ( "," ) + untrusted_ip + get_env ( "untrusted_ip", envp );
    
    newuser->setStatusFileKey(
        result != (const char*)NULL 
        ?
        string ( ":" ) + get_env ( "untrusted_port", envp )
        :
        string("") );
    

    This is one of the many cases where the conditional expression makes the code not only harder to read and understand, but also makes it wrong.

    The if else version is much easier to read and understand, and correct.