obj({ ...}) intialization, what is happening ?

I wrote something equivalent to the following by accident, and it worked. However, I am not sure what is going on exactly. Are the parentheses just ignored? What if I had defined a point(std:list_intializer<int,int> args) constructor, would the line point p ({34,36}); be interpreted differently.


1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
#include <iostream>

class point{
    public:
    int _x, _y;
    point(int x,int y ):_x(x),_y(y){}
};

int main(){

point p ({34,36});  //<----- what is this exactly. 

std::cout << p._x<<", "<<p._y << std::endl;

}
Last edited on
The parens do seem to be ignored.
With the initializer_list, that is the ctor that is called.
Otherwise the x,y version is seen to match.
Note that it works without either of them, too.
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
#include <iostream>
#include <initializer_list>

class point {
public:
    int _x, _y;
    point(int x, int y) : _x(x), _y(y) { std::cout << "point(x,y)\n"; }  // try commenting this out, too
#if 1   // set to 0 to remove
    point(std::initializer_list<int> args) : _x{}, _y{} {
        std::cout << "point(list)\n";
        auto i = args.begin();
        if (i != args.end()) {
            _x = *i;
            if (++i != args.end())
                _y = *i;
        }
    }
#endif
};

int main(){
    point p ({34, 36});
    std::cout << p._x << ", " << p._y << '\n';
}

It turns out that point p ({34, 36}); is a functional cast expression. It uses your point(int x,int y) ctor as an implicit cast. If you put explicit in front of that ctor then it won't use it.
Last edited on
point p ({34,36}); //<----- what is this exactly.
This is short for
point p (point{34,36});
coder777 wrote:
This is short for
point p (point{34,36});

No that doesn't seem to be the case.
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
class point {
public:
	int _x, _y;
	//point(int&& x, int&& y) :_x(x), _y(y) {}
	point() {
		_x = 34;
		_y = 36;
	}
	point(point& a) {
		_x = a._x;
		_y = a._y;
	}
};

int main() {

	point p1; // default constructor
	point p2(p1); // constructor found
	point p3({ 34, 36 }); // no appropriate constructor found

	std::cin.get();
}

However I don't know what's actually happening. Seems to be the same as calling with point p3{ 34, 36 };

EDIT: coder777 is right.
Last edited on
> Are the parentheses just ignored?

No.


> What if I had defined a point(std:list_intializer<int,int> args) constructor,
> would the line point p ({34,36}); be interpreted differently.

Yes.


1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
#include <iostream>
#include <initializer_list>

int main() {

    {
        class point {
            public:

                int _x, _y ;

                point( int x, int y ) : _x(x), _y(y) {}
        };

        // direct list initialisation
        point p1 { 34, 36 } ;

        // copy list initialisation
        point p2 ( { 34, 36 } ) ;
    }

    {
        class point {
            public:

                int _x, _y ;

                explicit point( int x, int y ) : _x(x), _y(y) {}
        };

        // direct list initialisation
        // https://en.cppreference.com/w/cpp/language/list_initialization#direct-list-initialization
        point p1 { 34, 36 } ; // fine

        // attempted copy list initialisation
        // https://en.cppreference.com/w/cpp/language/list_initialization#copy-list-initialization
        point p2 ( { 34, 36 } ) ; // *** error: no matching constructor

        // copy initialisation
        // https://en.cppreference.com/w/cpp/language/copy_initialization
        point p3( point { 34, 36 } ) ; // fine
    }

    {
        class point {
            public:

                int _x, _y ;

                explicit point( int x, int y ) : _x(x), _y(y) {}

                explicit point( std::initializer_list<int> il )
                    : _x( *il.begin() ), _y( *( il.begin()+1 ) ) {}
        };

        // direct list initialisation
        point p1 { 34, 36 } ; // fine

        // copy list initialisation (with copy elision)
        // (point constructed with a single argument of type std::initializer_list<int>)
        point p2 ( { 34, 36 } ) ; // fine

        // copy initialisation
        // https://en.cppreference.com/w/cpp/language/copy_initialization
        point p3( point { 34, 36 } ) ; // fine
    }
}

http://coliru.stacked-crooked.com/a/ab9d9fefef166d25
Thanks, everyone for the clarification. And so braces within {} or () are matched recursively with potential constructors, and all the following examples seem to work. It is as-if {} within () or {} meant "the object you can unambiguously build with the inner args that fits in this context"."

That gives c++ a Python feel!

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38

#include <iostream>

class point{
    public:
    point(int x, int y){}
};

class segment{
    public:
    segment(point x, point y){}
};

class plane{
    public:
    plane(point p, segment s){}
};


class line{
    public:
    line(std::initializer_list<point> ps){}
};

void  length(point p1, point p2){std::cout << "length(point p1, point p2)" << std::endl;}

void  length(segment s){std::cout << "length(segment s)" << std::endl;}

int main(){
    
point p {1,2};
segment s{{1,2},{2,4}};
plane pl {{1,2},{{3,4},{5,6}}};
line l {{1,2},{3,4},{5,6},{7,8}};
length({1,2},{2,4});;
length( {{1,2},{2,4}} );

}

Last edited on
Topic archived. No new replies allowed.