c++vectoruniform-initialization

Uniform Initialization to call a constructor other than initializer list


I am recently having a problem with uniform initialization on C++14 standard. I wrote a code which declares a class having two constructors, first one is parameterized constructor and the other one is a constructor having intializer_list. The code looks as follows,

#include <iostream>
#include <vector>

class MyClass final {
public:
    MyClass() = default;

    explicit MyClass(const int a, const int b)
        : m_a(a)
        , m_b(b)
    {
        std::cout << "From constructor" << std::endl;
    };

    MyClass(std::initializer_list<int> init_li)
        : m_a(init_li[0])
        , m_b(init_li[1])
    {
        std::cout << "Initializer List constructor" << std::endl;
    }

    void PrintValues() const
    {
        std::cout << m_a << " " << m_b << std::endl;
    }

private:
    int m_a, m_b;
}

int
main(void)
{
    using namespace std;

    vector<int> a{ 1, 2 }; // Creates a vector with initializer list. Very nice.
    vector<int> a{ (1, 2) }; // Calls a vector constructor "explicit vector(std::size_t __n, const std::allocator<int> &__a = std::vector<int>::allocator_type())" NOT INITIALIZER LIST constructor. Okay.

    MyClass a{ 1, 2 }; // Calls the initializer list constructor. Fine.
    MyClass b{ (1, 2) }; // Also calls the initializer list constructor. What???

    b.PrintValues();

    return 0;
}

So, my question is why can't I call a constructor other than initializer list with uniform initialization in MyClass just like I did in vector with parentheses?


Solution

  • This code:

    MyClass a2{ (1, 2) };
    

    could not call the 2 argument constructor. What is happening is that in the expression (1,2), the 1 is evaluated before the comma operator, it is discarded, and the result of the expression is 2. This obviously calls the initializer_list constructor.

    Enable warnings, e.g. with -Wall and the compiler will tell you about this.


    Note that the same thing happens in the vector<int> a{ (1, 2) }; call. The 1 is discarded, and the constructor of vector that takes a single argument is invoked.