Topic: How many parameters in T({x, y, z}) ?
Author: Scott Meyers <usenet@aristeia.com>
Date: Sat, 31 Oct 2009 00:58:15 CST Raw View
Consider the following attempt to construct a Widget object:
Widget w( {5, 10, 15} );
How many arguments are being passed to the constructor? I say one, and gcc 4.4
seems to agree with me:
#include <iostream>
#include <initializer_list>
class Widget {
public:
Widget(int, int, int) { std::cout << "3 ints\n"; }
private:
Widget(const Widget&) { std::cout << "copy\n"; }
};
int main()
{
Widget w ({ 10, 20, 30 }); // error! copy ctor is private
}
Presumably the compiler is trying to create a temporary from the 3-int
constructor to pass to the copy constructor, which it thinks I'm trying to call
by passing only one constructor argument.
If this is the case, adding a constructor taking an initializer_list should make
no difference: I'd still be calling a constructor with a single argument. The
initializer_list constructor would be called to create the temporary, but I'd
still have to have access to the copy constructor. But that's not what gcc does:
#include <iostream>
#include <initializer_list>
class Widget {
public:
Widget(int, int, int) { std::cout << "3 ints\n"; }
Widget(std::initializer_list<int>) { std::cout << "init list\n"; }
private:
Widget(const Widget&) { std::cout << "copy\n"; }
};
int main()
{
Widget w ({ 10, 20, 30 }); // compiles, runs, prints "init list"
}
Is this a bug in my understanding or a bug in gcc's implementation?
Thanks,
Scott
--
[ comp.std.c++ is moderated. To submit articles, try just posting with ]
[ your news-reader. If that fails, use mailto:std-c++@netlab.cs.rpi.edu]
[ --- Please see the FAQ before posting. --- ]
[ FAQ: http://www.comeaucomputing.com/csc/faq.html ]
Author: Sean Hunt <rideau3@gmail.com>
Date: Sat, 31 Oct 2009 22:05:59 CST Raw View
On Oct 31, 12:58 am, Scott Meyers <use...@aristeia.com> wrote:
> Is this a bug in my understanding or a bug in gcc's implementation?
>
> Thanks,
>
> Scott
When you call "Widget w({ 10, 20, 30 })", the compiler sees two
candidates for the constructor - one taking const Widget&, and one
taking three ints. Neither of those is a direct match, but it is
possible to list-initialize a temporary of type Widget using the
triple-int constructor, and then call the copy constructor. This
behavior seems odd for constructors, but it makes sense in the
following scenario:
void foo (int, int, int);
void foo (const Widget&);
int main() {
foo({10, 20, 30});
}
Would you really expect that call to foo to call the first declaration
of foo?
If you wish to initialize w directly from an initializer-list of three ints,
you should declare it as "Widget w {10, 20, 30};", which, as I understand it,
is the initialization syntax that will Do the Right Thing in nearly every
scenario.
Sean Hunt
--
[ comp.std.c++ is moderated. To submit articles, try just posting with ]
[ your news-reader. If that fails, use mailto:std-c++@netlab.cs.rpi.edu]
[ --- Please see the FAQ before posting. --- ]
[ FAQ: http://www.comeaucomputing.com/csc/faq.html ]
Author: "Johannes Schaub (litb)" <schaub-johannes@web.de>
Date: Sat, 31 Oct 2009 22:06:31 CST Raw View
Scott Meyers wrote:
> Consider the following attempt to construct a Widget object:
>
> Widget w( {5, 10, 15} );
>
> How many arguments are being passed to the constructor? I say one, and
> gcc 4.4 seems to agree with me:
>
> #include <iostream>
> #include <initializer_list>
>
> class Widget {
> public:
> Widget(int, int, int) { std::cout << "3 ints\n"; }
> private:
> Widget(const Widget&) { std::cout << "copy\n"; }
> };
>
> int main()
> {
> Widget w ({ 10, 20, 30 }); // error! copy ctor is private
> }
>
> Presumably the compiler is trying to create a temporary from the 3-int
> constructor to pass to the copy constructor, which it thinks I'm trying to
> call by passing only one constructor argument.
>
> If this is the case, adding a constructor taking an initializer_list
> should make
> no difference: I'd still be calling a constructor with a single argument.
> The initializer_list constructor would be called to create the temporary,
> but I'd
> still have to have access to the copy constructor. But that's not what
> gcc does:
>
> #include <iostream>
> #include <initializer_list>
>
> class Widget {
> public:
> Widget(int, int, int) { std::cout << "3 ints\n"; }
> Widget(std::initializer_list<int>) { std::cout << "init list\n"; }
> private:
> Widget(const Widget&) { std::cout << "copy\n"; }
> };
>
> int main()
> {
> Widget w ({ 10, 20, 30 }); // compiles, runs, prints "init list"
> }
>
> Is this a bug in my understanding or a bug in gcc's implementation?
>
There is a subtle difference that i think is worth noting so it's clear:
Widget w{10, 20, 30};
Is different from the following
Widget w({10, 20, 30});
The first is list-initialization, and will end up in 13.3.1.7 for finding
candidates, and the second is direct initialization (because the initializer
is a braced expression list), and ends up at 13.3.1.3. So in fact the
argument in your case consists of 1 instead of 3 arguments - so only the
copy constructor is viable. For this, 13.3.3.1.5 does the mapping to the
three forms of implicit conversion sequences, and selects 13.3.3.1.5/5, and
the reference binding will make the conversion sequence a user defined
conversion sequence by going back to 13.3.3.1.5/3.
Now, if you add the initializer list, there are two viable candidate
functions, instead of only one: The initializer list constructor, and the
copy constructor. 13.3.3.1.5 maps the conversion sequence for the
initializer list constructor to an identity conversion by 13.3.3.1.5/2. In
this case, the copy constructor is still viable, but reference binding is
going back to 13.3.3.1.5/3 again (because it converts the initializer list
to "Widget" - *not* to initializer_list<int> which is merely a parameter of
the constructor used in the user defind conversion sequence.
Since using the initializer list ctor results in a better conversion
sequence, it's used over the copy constructor by GCC. I think GCC does it
all right here.
--
[ comp.std.c++ is moderated. To submit articles, try just posting with ]
[ your news-reader. If that fails, use mailto:std-c++@netlab.cs.rpi.edu]
[ --- Please see the FAQ before posting. --- ]
[ FAQ: http://www.comeaucomputing.com/csc/faq.html ]
Author: Scott Meyers <usenet@aristeia.com>
Date: Sun, 1 Nov 2009 21:26:58 CST Raw View
Johannes Schaub (litb) wrote:
> is a braced expression list), and ends up at 13.3.1.3. So in fact the
> argument in your case consists of 1 instead of 3 arguments
[...]
> Now, if you add the initializer list, there are two viable candidate
> functions, instead of only one: The initializer list constructor, and the
> copy constructor. 13.3.3.1.5 maps the conversion sequence for the
> initializer list constructor to an identity conversion by 13.3.3.1.5/2. In
> this case, the copy constructor is still viable, but reference binding is
> going back to 13.3.3.1.5/3 again (because it converts the initializer list
> to "Widget" - *not* to initializer_list<int> which is merely a parameter of
> the constructor used in the user defind conversion sequence.
>
> Since using the initializer list ctor results in a better conversion
> sequence, it's used over the copy constructor by GCC. I think GCC does it
> all right here.
Given your (very impressive) analysis, I agree. It will take some time before I
internalize that
Widget w { a, b, c };
is a constructor call with 1 or 3 arguments (depending on the constructors
offered by Widget), while
Widget ({a, b, c});
is always a constructor call with 1 argument.
Scott
--
[ comp.std.c++ is moderated. To submit articles, try just posting with ]
[ your news-reader. If that fails, use mailto:std-c++@netlab.cs.rpi.edu]
[ --- Please see the FAQ before posting. --- ]
[ FAQ: http://www.comeaucomputing.com/csc/faq.html ]