Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Strange constructor behavior

I am using Visual Studio 2013, and this is what I'm trying to figure out:

#include <iostream>
#include <vector>
using namespace std;

class A
{
public:
    int x = 1;
    bool y = true;

    A(int _x, bool _y) : x(_x), y(_y)
    {
        cout << "Constructor #1" << endl;
    }

    A(std::initializer_list<int> init_list)
    {
        cout << "Constructor #2" << endl;
    }
};


int main(int argc, char** argv)
{
    A Aobj1(10, false);
    A Aobj2(20, "false");
    A Aobj3{30, false};
    A Aobj4{40, "false"};

    return 0;
}

The output is:

Constructor #1
Constructor #1
Constructor #2
Constructor #1
  • The first call to constructor #1 is fine

  • Now the second construction of Aobj2(int, string) calling constructor #1 is strange. How is the compiler calling the (int, bool) constructor with a string argument? The "false" bool is not even converted to an int.

  • Aobj3 is also OK. Although the initializer_list is of type int, the compiler calls this because of brace-initialization and converts the bool to an int.

  • This one again baffles me. I would have expected this to be an error because a string cannot be converted to an int (as it was with the bool), and also expected the compiler to be calling the initializer_list constructor because it is a braced initialization. But the compiler chooses the (int, bool) constructor.

What is some background on the logic of the compiler?

like image 666
madu Avatar asked Dec 06 '22 10:12

madu


2 Answers

Now the second construction of Aobj2(int, string) calling constructor #1 is strange. How is the compiler calling the (int, bool) constructor with a string argument?

More precisely, "false" is of type const char[6] and could decay to const char*, i.e. a pointer, and then could implicitly convert to bool.

A prvalue of integral, floating-point, unscoped enumeration, pointer, and pointer-to-member types can be converted to a prvalue of type bool.

The value zero (for integral, floating-point, and unscoped enumeration) and the null pointer and the null pointer-to-member values become false. All other values become true.

For the 4th case, the parameter std::initializer_list<int> can't match the argument 40, "false", because there're no implicit conversion converting pointer to int. Then the constructor taking int, bool is selected, because "false" could be converted to bool implicitly as the above explained.

like image 132
songyuanyao Avatar answered Dec 27 '22 11:12

songyuanyao


"false" is a const char[6] which decays to const char* and pointers are implicitly convertible to bool. That's why the compiler can call your constructor taking a int and a bool, since the compiler is allowed to perform one implicit conversion to make a function argument match.

like image 33
Jesper Juhl Avatar answered Dec 27 '22 13:12

Jesper Juhl