Using std::initializer_list?

230 مرّة مشاهدة
التخطي إلى أول رسالة غير مقروءة

Matthew Dempsky

غير مقروءة،
23‏/11‏/2015، 7:17:50 م23‏/11‏/2015
إلى cxx
Can we allow use of std::initializer_list and/or STL functionality that relies on it?

E.g. I think this is nice:

diff --git a/base/linux_util.cc b/base/linux_util.cc
index d94588f..1acf5aa 100644
--- a/base/linux_util.cc
+++ b/base/linux_util.cc
@@ -102,11 +102,8 @@ std::string GetLinuxDistro() {
   // We do this check only once per process. If it fails, there's
   // little reason to believe it will work if we attempt to run
   // lsb_release again.
-  std::vector<std::string> argv;
-  argv.push_back("lsb_release");
-  argv.push_back("-d");
   std::string output;
-  GetAppOutput(CommandLine(argv), &output);
+  GetAppOutput(CommandLine({"lsb_release", "-d"}), &output);
   if (output.length() > 0) {
     // lsb_release -d should return: Description:<tab>Distro Info
     const char field[] = "Description:\t";

I expect there are more instances that could be cleaned up similarly.  That's just the first I found off hand.


Relatedly, one of the reasons https://groups.google.com/a/chromium.org/forum/#!topic/chromium-dev/GF96FshwHLw concluded to disallow "Uniform Initialization Syntax" was because it would behave differently depending on whether or not the standard library supported std::initializer_list.  However, note that the two features are (mostly) orthogonal: it's possible to use std::initializer_list without the "Uniform Initialization Syntax", and the above diff is NOT using "Uniform Initialization Syntax".

Vladimir Levin

غير مقروءة،
23‏/11‏/2015، 7:24:06 م23‏/11‏/2015
إلى Matthew Dempsky،cxx
Assuming this works everywhere, I would support the ability to use this (possibly with some guidance), but still disallow actually typing the words "std::initializer_list" anywhere :) This could be a nice way to ease into using the feature.

This, of course, modulo any actual issues with this syntax on any compiler that we use.

--
You received this message because you are subscribed to the Google Groups "cxx" group.
To unsubscribe from this group and stop receiving emails from it, send an email to cxx+uns...@chromium.org.
To post to this group, send email to c...@chromium.org.
To view this discussion on the web visit https://groups.google.com/a/chromium.org/d/msgid/cxx/CAF52%2BS7kS09xwjoLngRVS_w9aZG%3DedrPNQrbjaq-SK-zg-eKoA%40mail.gmail.com.

Sam McNally

غير مقروءة،
23‏/11‏/2015، 7:38:54 م23‏/11‏/2015
إلى Vladimir Levin،Matthew Dempsky،cxx
I'd prefer allowing initializer_list and uniform initialization syntax at the same time. The distinction is not meaningful as a user.

https://codereview.chromium.org/1467963002/ demonstrates both compiling on the standard bots.

Nico Weber

غير مقروءة،
23‏/11‏/2015، 8:14:05 م23‏/11‏/2015
إلى Sam McNally،Vladimir Levin،Matthew Dempsky،cxx
As mentioned in the thread Matthew linked to, uniform initialization syntax can cause silent miscompiles in msvc, e.g. https://codereview.chromium.org/1130873007/#msg13 So it seems that toolchains aren't there yet for uniform initialization syntax. (That repro doesn't use initializer_lists.)

Matthew, in your example the vector(std::initializer_list) constructor is getting called, right? At least item 7 in "Effective Modern C++" considers that to be one example of "braced initialization" (how the author calls uniform initialization) – given that initializing aggregates from braced constructs is buggy in MSVC2013 I'm not sure why we should believe that constructor overload resolution isn't – I imagine fairly similar code runs in the compiler in both cases.

Maybe the argument could be made that using the initializer_list constructors of map and vector have seen extensive testing and work well. So if we checked if this is in fact true (with programs similar to the one I linked above, but with a vector thrown in somewhere), maybe we could say "ok to use for initializing vector<>s and map<>s" until we have a better compiler on Windows.

Peter Kasting

غير مقروءة،
23‏/11‏/2015، 8:20:06 م23‏/11‏/2015
إلى Nico Weber،Sam McNally،Vladimir Levin،Matthew Dempsky،cxx
On Mon, Nov 23, 2015 at 5:14 PM, Nico Weber <tha...@chromium.org> wrote:
maybe we could say "ok to use for initializing vector<>s and map<>s" until we have a better compiler on Windows.

Do we know if MSVC 2015 handles this better?  If it does, I'd be inclined to say let's just hold off on all of this until we have that (hopefully not all that long).

PK 

Daniel Cheng

غير مقروءة،
23‏/11‏/2015، 8:20:19 م23‏/11‏/2015
إلى Nico Weber،Sam McNally،Vladimir Levin،Matthew Dempsky،cxx
Since it's a silent miscompile, it seems easy to overlook by mistake. =(
Is this fixed in VS2015?

Daniel

Nico Weber

غير مقروءة،
23‏/11‏/2015، 8:26:53 م23‏/11‏/2015
إلى Peter Kasting،Sam McNally،Vladimir Levin،Matthew Dempsky،cxx،Bruce Dawson
On Mon, Nov 23, 2015 at 8:20 PM, Peter Kasting <pkas...@google.com> wrote:
On Mon, Nov 23, 2015 at 5:14 PM, Nico Weber <tha...@chromium.org> wrote:
maybe we could say "ok to use for initializing vector<>s and map<>s" until we have a better compiler on Windows.

Do we know if MSVC 2015 handles this better?

Bruce reported that https://codereview.chromium.org/1130873007/#msg13 was fixed with some prerelease version of 2015 he had back then. Unless it regressed again before the final release, it should be fixed.

Matthew Dempsky

غير مقروءة،
23‏/11‏/2015، 8:40:40 م23‏/11‏/2015
إلى Sam McNally،Vladimir Levin،cxx
On Mon, Nov 23, 2015 at 4:38 PM, Sam McNally <sa...@chromium.org> wrote:
I'd prefer allowing initializer_list and uniform initialization syntax at the same time. The distinction is not meaningful as a user.

Why do you say the distinction is not meaningful?  Just because they both use curly braces for their syntax?  They're separate features.

hend...@google.com

غير مقروءة،
23‏/11‏/2015، 8:41:57 م23‏/11‏/2015
إلى cxx،sa...@chromium.org،vmp...@google.com،mdem...@chromium.org
That MSVC issue does not affect initializer_list.

e.g.  This works correctly:

void f(const std::vector<std::vector<std::string>>& s);

f({{"1"}, {"a", "b"}});

Peter Kasting

غير مقروءة،
23‏/11‏/2015، 8:43:24 م23‏/11‏/2015
إلى Matthew Dempsky،Sam McNally،Vladimir Levin،cxx
I interpreted it as "most programmers won't see these two different features as different things but will mentally lump them together as "uniform initializer syntax".  That's certainly true in my case.

PK 

Matthew Dempsky

غير مقروءة،
23‏/11‏/2015، 8:51:12 م23‏/11‏/2015
إلى Peter Kasting،Sam McNally،Vladimir Levin،cxx
On Mon, Nov 23, 2015 at 5:43 PM, Peter Kasting <pkas...@google.com> wrote:
I interpreted it as "most programmers won't see these two different features as different things but will mentally lump them together as "uniform initializer syntax".  That's certainly true in my case.

This is a function call to Foo using a std::initializer_list:

    void Foo(std::initializer_list<int>);
    void F() {
      Foo({1,2,3});
    }

This is a call to the Bar constructor using uniform initialization:

    struct Bar { Bar(int a, int b, int c); };
    void G() {
      Bar{1,2,3};
    }

Peter Kasting

غير مقروءة،
23‏/11‏/2015، 8:55:34 م23‏/11‏/2015
إلى Matthew Dempsky،Sam McNally،Vladimir Levin،cxx
I'm not disputing that they're technically distinct.  I'm just agreeing with Sam that I wouldn't see those two things in a program and mentally think of two different things.

It's a lot easier to just tell people "{a, b, c} syntax is fine" than to try to make the distinction clear.

PK 

Sam McNally

غير مقروءة،
23‏/11‏/2015، 11:05:09 م23‏/11‏/2015
إلى Peter Kasting،Matthew Dempsky،Vladimir Levin،cxx
This is what I meant; thanks for clarifying Peter. My concerns are more around the initializer_list constructor vs. non-initializer_list constructor called using {}. Consider
void Foo(std::vector<int>);
void Bar(std::pair<int, int>);

Then
Foo({1, 2});
would call an initializer_list constructor, but
Bar({1, 2});
would not. 

PK 

Bruce Dawson

غير مقروءة،
24‏/11‏/2015، 3:26:45 ص24‏/11‏/2015
إلى Nico Weber،Peter Kasting،Sam McNally،Vladimir Levin،Matthew Dempsky،cxx
I'm running VC++ 2015 Update 1 (not yet publicly released) which is the version we're most likely to switch to. Is there a specific test I can run to help with this?

bruce...@google.com

غير مقروءة،
24‏/11‏/2015، 8:37:25 م24‏/11‏/2015
إلى cxx،pkas...@google.com،mdem...@chromium.org،vmp...@google.com
I just tested the code in https://codereview.chromium.org/1130873007/#msg13 with the latest VS 2015 and it handles it correctly. Sample size: 1. Success rate: 100%.

Peter Kasting

غير مقروءة،
10‏/12‏/2015، 5:06:13 م10‏/12‏/2015
إلى Matthew Dempsky،Sam McNally،Vladimir Levin،cxx
On Mon, Nov 23, 2015 at 5:55 PM, Peter Kasting <pkas...@google.com> wrote:
I'm not disputing that they're technically distinct.  I'm just agreeing with Sam that I wouldn't see those two things in a program and mentally think of two different things.

It's a lot easier to just tell people "{a, b, c} syntax is fine" than to try to make the distinction clear.

I retract this.

With a couple more weeks of perspective,

* The benefits of std::initializer_list seem sufficiently large that I would prefer not to wait longer
* It's not that hard to make the feature list clear on this:

ALLOWED
std::initializer_list  |  Practically speaking, this means you can use brace-style initialization with STL containers that have such a constructor, e.g. std::vector<int> v{1, 2, 3}.  See "Uniform initialization syntax" for the larger question of brace-style initialization everywhere.

BANNED
Uniform initialization syntax  |  struct S { int a; int b; } s{1, 2}; and the like are banned.  There is an exception for objections with a constructor that takes std::initializer_list; see notes on that.  |  Has bugs in MSVC 2013, revisit when we use MSVC 2015

Therefore, I renew the request to allow this feature.

PK

Vladimir Levin

غير مقروءة،
10‏/12‏/2015، 5:20:37 م10‏/12‏/2015
إلى Peter Kasting،Matthew Dempsky،Sam McNally،cxx
I would still caution against allowing typing the words std::initializer_list. People tend to treat them as containers but they are not. Consider this:

std::initializer_list<int> f() { return {1, 2, 3}; }
int main() {
  std::vector<int> a(f());
  printf ("%d %d %d\n", a[0], a[1], a[2]);
}

$ clang++ -std=c++11 -Wall test.cpp
$ ./a.out 
32767 -54118060 32767

This is just an example where returning an intializer_list is very wrong. I don't think this request will prevent any sane usages such as initializing a vector using brace style initialization.

Vlad

Peter Kasting

غير مقروءة،
10‏/12‏/2015، 5:26:51 م10‏/12‏/2015
إلى Vladimir Levin،Matthew Dempsky،Sam McNally،cxx
On Thu, Dec 10, 2015 at 2:20 PM, Vladimir Levin <vmp...@google.com> wrote:
I would still caution against allowing typing the words std::initializer_list. People tend to treat them as containers but they are not. Consider this:

std::initializer_list<int> f() { return {1, 2, 3}; }
int main() {
  std::vector<int> a(f());
  printf ("%d %d %d\n", a[0], a[1], a[2]);
}

$ clang++ -std=c++11 -Wall test.cpp
$ ./a.out 
32767 -54118060 32767

This is just an example where returning an intializer_list is very wrong. I don't think this request will prevent any sane usages such as initializing a vector using brace style initialization.

I don't think this is something we need to call out, as it's not specific to Chromium style or to our restrictions on the subset of C++11 to use.  This is basically "don't write code that is guaranteed to misbehave", much like:

  int& foo() {
    int x =3;
    return x;  // Uh oh
  }

If you really want, though, we could probably add in the notes for this "std::iniailizer_list is not a container; see <...>".

PK

Matthew Dempsky

غير مقروءة،
10‏/12‏/2015، 5:27:24 م10‏/12‏/2015
إلى Vladimir Levin،Peter Kasting،Sam McNally،cxx
I would like to be able to type std::initializer_list.  Can we not just set rules for when it's safe to use?  Something like "only use std::initializer_list as a function parameter type"?

On Thu, Dec 10, 2015 at 2:20 PM, Vladimir Levin <vmp...@google.com> wrote:

Vladimir Levin

غير مقروءة،
10‏/12‏/2015، 5:30:31 م10‏/12‏/2015
إلى Matthew Dempsky،Peter Kasting،Sam McNally،cxx
As long as we have some documentation that describes safe and unsafe usages, I think it's fine.

I agree that it's very much like returning a reference to a local, but since std::initializer_list is a newer concept than references, I think there's at least some people who don't know that returning it isn't safe (I was one of these people until relatively recently).

On Thu, Dec 10, 2015 at 2:27 PM, Matthew Dempsky <mdem...@chromium.org> wrote:
I would like to be able to type std::initializer_list.  Can we not just set rules for when it's safe to use?  Something like "only use std::initializer_list as a function parameter type"?

I guess I would squint and question a CL that has initializer_list as a parameter, but sure, there are probably cases where it's the best option.

Jeremy Roman

غير مقروءة،
10‏/12‏/2015، 5:59:16 م10‏/12‏/2015
إلى Vladimir Levin،Matthew Dempsky،Peter Kasting،Sam McNally،cxx
Are there common uses for writing "std::initializer_list" other than constructor (and possibly assignment operator) arguments? We could provide guidance that other uses should be examined closely, though the Google C++ Style Guide doesn't place that kind of restrictions on it.

--
You received this message because you are subscribed to the Google Groups "cxx" group.
To unsubscribe from this group and stop receiving emails from it, send an email to cxx+uns...@chromium.org.
To post to this group, send email to c...@chromium.org.

Dana Jansens

غير مقروءة،
10‏/12‏/2015، 6:05:20 م10‏/12‏/2015
إلى Jeremy Roman،Vladimir Levin،Matthew Dempsky،Peter Kasting،Sam McNally،cxx
On Thu, Dec 10, 2015 at 2:59 PM, Jeremy Roman <jbr...@chromium.org> wrote:
Are there common uses for writing "std::initializer_list" other than constructor (and possibly assignment operator) arguments?

Good question.
 
We could provide guidance that other uses should be examined closely, though the Google C++ Style Guide doesn't place that kind of restrictions on it.

If the answer to the above is no, I think giving guidance like that will be very helpful for people learning how to use the feature, without banning other uses.
 

Matthew Dempsky

غير مقروءة،
10‏/12‏/2015، 6:29:38 م10‏/12‏/2015
إلى Dana Jansens،Jeremy Roman،Vladimir Levin،Peter Kasting،Sam McNally،cxx
My pet use case for wanting to write std::initializer_list<T> is I have a member function that I want to be a type-safe varargs function.  E.g., calls like foo.Bar({a, b, c}, x).

I could use a variadic function template to make the function call look like foo.Bar(a, b, c, x) or foo.Bar(x, a, b, c), but I think grouping the a,b,c expressions is helpful to the API's readability and clang-format'ability.

I could also just use a std::vector<T> parameter and foo.Bar({a, b, c}, x) would still work (once std::initializer_list is allowed), but it would involve the unnecessary construction of a std::vector<T> to hold the temporary values.

(At the moment, callers write foo.BAR((a, b, c), x), which uses macro tricks and GCC extensions to construct a temporary std::vector<__typeof(a)> to pass to an underlying BarImpl member function.  This fails though when __typeof(a) can't represent b or c.  Also, it's just generally ugly and horrible.)

Peter Kasting

غير مقروءة،
10‏/12‏/2015، 6:44:49 م10‏/12‏/2015
إلى Matthew Dempsky،Dana Jansens،Jeremy Roman،Vladimir Levin،Sam McNally،cxx
On Thu, Dec 10, 2015 at 3:29 PM, Matthew Dempsky <mdem...@chromium.org> wrote:
My pet use case for wanting to write std::initializer_list<T> is I have a member function that I want to be a type-safe varargs function.  E.g., calls like foo.Bar({a, b, c}, x).

I could use a variadic function template to make the function call look like foo.Bar(a, b, c, x) or foo.Bar(x, a, b, c), but I think grouping the a,b,c expressions is helpful to the API's readability and clang-format'ability.

I could also just use a std::vector<T> parameter and foo.Bar({a, b, c}, x) would still work (once std::initializer_list is allowed), but it would involve the unnecessary construction of a std::vector<T> to hold the temporary values.

Ultimately it would be up to what your function is doing with these values, but the point of an "initializer list" is, from its name, to initialize something, not to serve as a general tuple, container, or parameter pack.  If you want to pass a bunch of values to a function you should generally be using one of those latter things.

PK 

Vladimir Levin

غير مقروءة،
10‏/12‏/2015، 6:47:26 م10‏/12‏/2015
إلى Peter Kasting،Matthew Dempsky،Dana Jansens،Jeremy Roman،Sam McNally،cxx
+1 
 


PK 

Matthew Dempsky

غير مقروءة،
10‏/12‏/2015، 6:59:24 م10‏/12‏/2015
إلى Peter Kasting،Dana Jansens،Jeremy Roman،Vladimir Levin،Sam McNally،cxx
On Thu, Dec 10, 2015 at 3:44 PM, Peter Kasting <pkas...@google.com> wrote:
Ultimately it would be up to what your function is doing with these values, but the point of an "initializer list" is, from its name, to initialize something,

I feel like this is going to get into a philosophical argument over what the meaning of "to initialize something" is.  I *would* be using those std::initializer_list<T> values to construct a list of objects in the Bar member function, but that's an implementation detail that I don't think is immediately relevant to the callers.

Vladimir Levin

غير مقروءة،
10‏/12‏/2015، 7:10:46 م10‏/12‏/2015
إلى Matthew Dempsky،Peter Kasting،Dana Jansens،Jeremy Roman،Sam McNally،cxx
To be honest, I think all of the initializer_list uses should be handled case-by-case. However, I completely agree with Peter that it shouldn't be treated as a container. As another example if you had code like:

void Bar(std::initializer_list<int> list, int c); // (this is a sample definition for your Bar({1, 2, 3}, 4) use case)

and you wanted to change it to also be able to take a vector and a list, then you could templatize it:

template <typename C>
void Bar(const C& container, int c);

However, now Bar({1, 2, 3}, 4); doesn't compile anymore, because {1, 2, 3} doesn't on its own designate any particular container. It's just used to initialize one.

All that being said, if you would never want to allow vectors/lists in place of the initializer_list (mostly because you use the initializer list to directly initialize a container), then that's fine.


Peter Kasting

غير مقروءة،
14‏/12‏/2015، 4:28:09 م14‏/12‏/2015
إلى cxx
So, is there support for allowing initializer_list under the following conditions?:

* Uniform initialization syntax in general is still banned.
* In general, this should be used to brace-initialize STL containers.
* The only case where you should write "std::initializer_list" is for arguments to constructor/assignment functions for custom containers.  If you want to pass or return multiple values in any other case, use an actual container, base::Tuple, or some other method.

This last might be slightly more conservative than is necessary, but it avoids the possibility of problems.  We could perhaps eliminate this specific guidance in the long run when people have more experience with initializer lists.

PK

Matthew Dempsky

غير مقروءة،
14‏/12‏/2015، 4:34:37 م14‏/12‏/2015
إلى Peter Kasting،cxx
On Mon, Dec 14, 2015 at 1:28 PM, 'Peter Kasting' via cxx <c...@chromium.org> wrote:
* The only case where you should write "std::initializer_list" is for arguments to constructor/assignment functions for custom containers.  If you want to pass or return multiple values in any other case, use an actual container, base::Tuple, or some other method.

I still think the restriction to constructor/assignment functions specifically is unnecessary, and std::initializer_list should be safe in any function's parameters list.

But I'm happy to have std::initializer_list at all as a start.

Vladimir Levin

غير مقروءة،
14‏/12‏/2015، 4:39:35 م14‏/12‏/2015
إلى Matthew Dempsky،Peter Kasting،cxx
I support this. I think we can transition to any function parameter over time, I'm just more paranoid than anything.

--
You received this message because you are subscribed to the Google Groups "cxx" group.
To unsubscribe from this group and stop receiving emails from it, send an email to cxx+uns...@chromium.org.
To post to this group, send email to c...@chromium.org.

Nico Weber

غير مقروءة،
14‏/12‏/2015، 4:43:48 م14‏/12‏/2015
إلى Peter Kasting،cxx
On Mon, Dec 14, 2015 at 4:28 PM, 'Peter Kasting' via cxx <c...@chromium.org> wrote:
So, is there support for allowing initializer_list under the following conditions?:

* Uniform initialization syntax in general is still banned.
* In general, this should be used to brace-initialize STL containers.
* The only case where you should write "std::initializer_list" is for arguments to constructor/assignment functions for custom containers.  If you want to pass or return multiple values in any other case, use an actual container, base::Tuple, or some other method.

This one is risky, as it'll encourage people to add constructors taking std::initializer_list<> to existing constructors, which can silently change the behavior of existing code.

And given the known codegen bugs of 2013 with initialization, brace-initialization STL containers of production code feels risky to me too, especially with 2015 not being too far from ready.

I'd be fine with "You can brace-initialize std::vector and std::map" or something to that effect while we're still on 2013.
 
This last might be slightly more conservative than is necessary, but it avoids the possibility of problems.  We could perhaps eliminate this specific guidance in the long run when people have more experience with initializer lists.

PK

--
You received this message because you are subscribed to the Google Groups "cxx" group.
To unsubscribe from this group and stop receiving emails from it, send an email to cxx+uns...@chromium.org.
To post to this group, send email to c...@chromium.org.

Peter Kasting

غير مقروءة،
14‏/12‏/2015، 4:48:44 م14‏/12‏/2015
إلى Nico Weber،cxx
On Mon, Dec 14, 2015 at 1:43 PM, Nico Weber <tha...@chromium.org> wrote:
On Mon, Dec 14, 2015 at 4:28 PM, 'Peter Kasting' via cxx <c...@chromium.org> wrote:
So, is there support for allowing initializer_list under the following conditions?:

* Uniform initialization syntax in general is still banned.
* In general, this should be used to brace-initialize STL containers.
* The only case where you should write "std::initializer_list" is for arguments to constructor/assignment functions for custom containers.  If you want to pass or return multiple values in any other case, use an actual container, base::Tuple, or some other method.

This one is risky, as it'll encourage people to add constructors taking std::initializer_list<> to existing constructors, which can silently change the behavior of existing code.

Only if people were already using uniform initialization syntax, right?  Which they shouldn't be.  Or are there cases in today's codebase this causes problems with?

(Also, the number of cases where people are writing new custom containers seems low, so it seems unlikely this would result in lots of such newly-added constructors.)

And given the known codegen bugs of 2013 with initialization, brace-initialization STL containers of production code feels risky to me too, especially with 2015 not being too far from ready.

I'm confused.  I thought we had determined that brace-initializing STL containers taking initializer_list did not trigger any codegen bugs, and that the issue was with uniform initialization syntax.

I'd be fine with "You can brace-initialize std::vector and std::map" or something to that effect while we're still on 2013.

How is this different than allowing brace-init of any STL container?  Why call out vector and map specifically?

PK 

Jeremy Roman

غير مقروءة،
14‏/12‏/2015، 4:49:00 م14‏/12‏/2015
إلى Nico Weber،Peter Kasting،cxx
On Mon, Dec 14, 2015 at 4:43 PM, Nico Weber <tha...@chromium.org> wrote:
On Mon, Dec 14, 2015 at 4:28 PM, 'Peter Kasting' via cxx <c...@chromium.org> wrote:
So, is there support for allowing initializer_list under the following conditions?:

* Uniform initialization syntax in general is still banned.
* In general, this should be used to brace-initialize STL containers.
* The only case where you should write "std::initializer_list" is for arguments to constructor/assignment functions for custom containers.  If you want to pass or return multiple values in any other case, use an actual container, base::Tuple, or some other method.

This one is risky, as it'll encourage people to add constructors taking std::initializer_list<> to existing constructors, which can silently change the behavior of existing code.

And given the known codegen bugs of 2013 with initialization, brace-initialization STL containers of production code feels risky to me too, especially with 2015 not being too far from ready.

I'd be fine with "You can brace-initialize std::vector and std::map" or something to that effect while we're still on 2013.

What about the braced-init-list case in the range-based for loop, i.e.

for (int i : {1,2,3}) { ... }

technically this uses std::initializer_list (because there's a special rule that makes it deduce to that here).
 
This last might be slightly more conservative than is necessary, but it avoids the possibility of problems.  We could perhaps eliminate this specific guidance in the long run when people have more experience with initializer lists.

PK

--
You received this message because you are subscribed to the Google Groups "cxx" group.
To unsubscribe from this group and stop receiving emails from it, send an email to cxx+uns...@chromium.org.
To post to this group, send email to c...@chromium.org.
To view this discussion on the web visit https://groups.google.com/a/chromium.org/d/msgid/cxx/CAAHOzFAZP1qoP2brArkTsCn7HD2BaWCzqCfhL82B9OyFJsyPsQ%40mail.gmail.com.

--
You received this message because you are subscribed to the Google Groups "cxx" group.
To unsubscribe from this group and stop receiving emails from it, send an email to cxx+uns...@chromium.org.
To post to this group, send email to c...@chromium.org.

Sam McNally

غير مقروءة،
14‏/12‏/2015، 7:09:54 م14‏/12‏/2015
إلى Nico Weber،Peter Kasting،cxx
On Tue, 15 Dec 2015 at 08:43 Nico Weber <tha...@chromium.org> wrote:
On Mon, Dec 14, 2015 at 4:28 PM, 'Peter Kasting' via cxx <c...@chromium.org> wrote:
So, is there support for allowing initializer_list under the following conditions?:

* Uniform initialization syntax in general is still banned.
* In general, this should be used to brace-initialize STL containers.
* The only case where you should write "std::initializer_list" is for arguments to constructor/assignment functions for custom containers.  If you want to pass or return multiple values in any other case, use an actual container, base::Tuple, or some other method.

This one is risky, as it'll encourage people to add constructors taking std::initializer_list<> to existing constructors, which can silently change the behavior of existing code.

And given the known codegen bugs of 2013 with initialization, brace-initialization STL containers of production code feels risky to me too, especially with 2015 not being too far from ready.

I'd be fine with "You can brace-initialize std::vector and std::map" or something to that effect while we're still on 2013.

Is brace-initializing pairs for std::map's initializer_list-constructor safe?
 
This last might be slightly more conservative than is necessary, but it avoids the possibility of problems.  We could perhaps eliminate this specific guidance in the long run when people have more experience with initializer lists.

PK

--
You received this message because you are subscribed to the Google Groups "cxx" group.
To unsubscribe from this group and stop receiving emails from it, send an email to cxx+uns...@chromium.org.
To post to this group, send email to c...@chromium.org.
To view this discussion on the web visit https://groups.google.com/a/chromium.org/d/msgid/cxx/CAAHOzFAZP1qoP2brArkTsCn7HD2BaWCzqCfhL82B9OyFJsyPsQ%40mail.gmail.com.

--
You received this message because you are subscribed to the Google Groups "cxx" group.
To unsubscribe from this group and stop receiving emails from it, send an email to cxx+uns...@chromium.org.
To post to this group, send email to c...@chromium.org.

Vladimir Levin

غير مقروءة،
17‏/12‏/2015، 4:36:18 م17‏/12‏/2015
إلى Sam McNally،Nico Weber،Peter Kasting،cxx
On Mon, Dec 14, 2015 at 4:09 PM, Sam McNally <sa...@chromium.org> wrote:


On Tue, 15 Dec 2015 at 08:43 Nico Weber <tha...@chromium.org> wrote:
On Mon, Dec 14, 2015 at 4:28 PM, 'Peter Kasting' via cxx <c...@chromium.org> wrote:
So, is there support for allowing initializer_list under the following conditions?:

* Uniform initialization syntax in general is still banned.
* In general, this should be used to brace-initialize STL containers.
* The only case where you should write "std::initializer_list" is for arguments to constructor/assignment functions for custom containers.  If you want to pass or return multiple values in any other case, use an actual container, base::Tuple, or some other method.



I feel like this discussion is getting to the point where someone is going to propose a CL to allow this in some shape or form, so I wanted to verify that I have some idea of what the above three points mean. In particular, given the following.. which are OK and which are not OK? I'm particularly confused about uniform initialization vs brace initializing STL containers:

  std::vector<int> a{};
  std::vector<int> b = {}; 
  std::vector<int> c = {1, 2, 3}; 
  std::vector<int> d{1, 2, 3}; 

  int e[1] = {}; 
  int f[1]{};
  int g[1] = {1};
  int h[1]{1};

  int i{};
  int j = {}; 
  int k = {1};
  int l{1};

  m({1, 2, 3});
  n(std::vector{1, 2, 3});

All of these compile on clang on linux, but some are not OK, as I understand it. Can someone please make a one line comment about what's uniform initialization vs what's brace initialization vs default initialization? I'm using this as a reference, but the terminology is slightly different: http://herbsutter.com/2013/05/09/gotw-1-solution/



Peter Kasting

غير مقروءة،
17‏/12‏/2015، 4:57:14 م17‏/12‏/2015
إلى Vladimir Levin،Sam McNally،Nico Weber،cxx
On Thu, Dec 17, 2015 at 1:36 PM, Vladimir Levin <vmp...@google.com> wrote:
I feel like this discussion is getting to the point where someone is going to propose a CL to allow this in some shape or form, so I wanted to verify that I have some idea of what the above three points mean. In particular, given the following.. which are OK and which are not OK? I'm particularly confused about uniform initialization vs brace initializing STL containers:

I'll do my best, but might be wrong.

  std::vector<int> a{};
  std::vector<int> b = {}; 
  std::vector<int> c = {1, 2, 3}; 
  std::vector<int> d{1, 2, 3}; 

I think these all boil down to calling the initializer_list constructor for vector and are thus fine.

  int e[1] = {}; 
  int f[1]{};
  int g[1] = {1};
  int h[1]{1};

I think e and g are simply classic array initialization and are thus fine, while f and h require uniform initialization syntax and are thus not OK.

  int i{};
  int j = {}; 
  int k = {1};
  int l{1};

These would all be disallowed.

  m({1, 2, 3});
  n(std::vector{1, 2, 3});

n is OK.  m might be OK depending on its signature (I think the vector initializer_list constructor isn't explicit, so if m takes a vector this would be OK).

PK 

Daniel Cheng

غير مقروءة،
17‏/12‏/2015، 5:35:35 م17‏/12‏/2015
إلى Peter Kasting،Vladimir Levin،Sam McNally،Nico Weber،cxx
On Thu, Dec 17, 2015 at 1:57 PM 'Peter Kasting' via cxx <c...@chromium.org> wrote:
On Thu, Dec 17, 2015 at 1:36 PM, Vladimir Levin <vmp...@google.com> wrote:
I feel like this discussion is getting to the point where someone is going to propose a CL to allow this in some shape or form, so I wanted to verify that I have some idea of what the above three points mean. In particular, given the following.. which are OK and which are not OK? I'm particularly confused about uniform initialization vs brace initializing STL containers:

I'll do my best, but might be wrong.

  std::vector<int> a{};
  std::vector<int> b = {}; 
  std::vector<int> c = {1, 2, 3}; 
  std::vector<int> d{1, 2, 3}; 

I think these all boil down to calling the initializer_list constructor for vector and are thus fine.

Another confusing case (for me) is something like this:
  std::vector<int> v{10};
initializer_list wins, so the vector is constructed with a single element (10) rather than a vector of size 10.

Daniel
 

  int e[1] = {}; 
  int f[1]{};
  int g[1] = {1};
  int h[1]{1};

I think e and g are simply classic array initialization and are thus fine, while f and h require uniform initialization syntax and are thus not OK.

  int i{};
  int j = {}; 
  int k = {1};
  int l{1};

These would all be disallowed.

  m({1, 2, 3});
  n(std::vector{1, 2, 3});

n is OK.  m might be OK depending on its signature (I think the vector initializer_list constructor isn't explicit, so if m takes a vector this would be OK).

PK 

--
You received this message because you are subscribed to the Google Groups "cxx" group.
To unsubscribe from this group and stop receiving emails from it, send an email to cxx+uns...@chromium.org.
To post to this group, send email to c...@chromium.org.

Dana Jansens

غير مقروءة،
17‏/12‏/2015، 7:37:52 م17‏/12‏/2015
إلى Peter Kasting،Vladimir Levin،Sam McNally،Nico Weber،cxx
On Thu, Dec 17, 2015 at 1:57 PM, 'Peter Kasting' via cxx <c...@chromium.org> wrote:
On Thu, Dec 17, 2015 at 1:36 PM, Vladimir Levin <vmp...@google.com> wrote:
I feel like this discussion is getting to the point where someone is going to propose a CL to allow this in some shape or form, so I wanted to verify that I have some idea of what the above three points mean. In particular, given the following.. which are OK and which are not OK? I'm particularly confused about uniform initialization vs brace initializing STL containers:

I'll do my best, but might be wrong.

  std::vector<int> a{};
  std::vector<int> b = {}; 

This does not call the initializer_list constructor, this will call the default constructor, so that doesn't comply with the above rule.
 
  std::vector<int> c = {1, 2, 3}; 
  std::vector<int> d{1, 2, 3}; 

I think these all boil down to calling the initializer_list constructor for vector and are thus fine.

These 2 make an initializer list and call that constructor. FTR {1} would behave the same as {1,2,3} here.
 

  int e[1] = {}; 
  int f[1]{};
  int g[1] = {1};
  int h[1]{1};

I think e and g are simply classic array initialization and are thus fine, while f and h require uniform initialization syntax and are thus not OK.

  int i{};
  int j = {}; 
  int k = {1};
  int l{1};

These would all be disallowed.

  m({1, 2, 3});
  n(std::vector{1, 2, 3});

n is OK.  m might be OK depending on its signature (I think the vector initializer_list constructor isn't explicit, so if m takes a vector this would be OK).

m should be okay wherever it compiles, it will definitely make an initializer list, so m must either: take an initializer list, or take an object with implicit conversion from an initializer list.

I think one way to think about braces in a way that satisfies those rules is that you can use them in cases where there's no other way to write it without the braces? I do think this rule is approximately impossible to expect people to enforce correctly.


It gets especially problematic around |t5_2|, where it looks just like |t5|, but one is an initializer list, but one isn't.

And here's a good article about this: http://herbsutter.com/2013/05/09/gotw-1-solution/ that I read to come up with these examples.

Probably making more explicit where-it's-allowed is understandable/enforceable, if anything is. I liked Nico's and Jeremy's suggestions:
* Can be used to initialize standard containers.
* Can be used for hard-coded range-based for loops.

Daniel's point about std::vector<int> v{5} being different from std::vector<int> v(5) is interesting, and probably going to confuse people. 

If we all get used to f{a,b} always being an initializer_list (based on our restricted allowed usage), and then start using it for types other than standard containers (that don't have implicit initializer_list constructors), it's going to throw off the simpler intuitions people build around the syntax.

This is weird in a way like std::move() is weird - in that reading the callsite doesn't tell you what the code is doing. We're probably going to have to get used to this, eventually, but I don't think it has the same kinda of risk/reward tradeoffs that using std::move() has.

Vladimir Levin

غير مقروءة،
17‏/12‏/2015، 7:50:43 م17‏/12‏/2015
إلى Dana Jansens،Peter Kasting،Sam McNally،Nico Weber،cxx
This isn't necessarily true. It depends whether m takes an object that has a constructor that takes an initializer_list. For example, if you have

struct Foo {
  Foo(int a, int b, int c);
};

void m(Foo foo);

Then according to clang, this doesn't create an initializer_list:

m({1, 2, 3});

I just think it's easy to think that you're calling one ctor and end up calling another ctor... I would like to allow uniform initialization syntax in the same breath as allowing initializer_list initialization. If allowing this all unconditionally is blocked on MSVC2015, then maybe it's worth just waiting for MSVC2015? Either that or maybe a rule / note / strong suggestion that was similar to the previous rvalue reference rule: have a style guide owner approve usage.

Jeffrey Yasskin

غير مقروءة،
17‏/12‏/2015، 7:52:00 م17‏/12‏/2015
إلى Dana Jansens،Peter Kasting،Vladimir Levin،Sam McNally،Nico Weber،cxx
If m is:

struct MyType {
  MyType(int, long, double);
};
void m(MyType);

This looks a lot like the MSVC miscompile in https://codereview.chromium.org/1130873007/#msg13. That makes me nervous about allowing any of these until we go to MSVC 2015. At most, an initialization of a class-whose-name-makes-it-clearly-a-container could make sense.
 
Daniel's point about std::vector<int> v{5} being different from std::vector<int> v(5) is interesting, and probably going to confuse people. 

The way to think about this one is that you use ()s to use an algorithm to initialize an object, and you use {} to list the contents of the object explicitly. The language allows you to call an algorithmic constructor with {}s or a member-listing constructor with ()s, but we should stylistically prefer not to, and doing that will help us think correctly about places like this where the language has to disambiguate between multiple constructors.

Jeffrey

Peter Kasting

غير مقروءة،
17‏/12‏/2015، 8:02:32 م17‏/12‏/2015
إلى Dana Jansens،Vladimir Levin،Sam McNally،Nico Weber،cxx
On Thu, Dec 17, 2015 at 4:37 PM, Dana Jansens <dan...@chromium.org> wrote:
  m({1, 2, 3});
  n(std::vector{1, 2, 3});

n is OK.  m might be OK depending on its signature (I think the vector initializer_list constructor isn't explicit, so if m takes a vector this would be OK).

m should be okay wherever it compiles, it will definitely make an initializer list, so m must either: take an initializer list, or take an object with implicit conversion from an initializer list.

Vladimir and Jeffrey explained part of the reasoning for why I didn't blanket-allow this.  The other part is that writing something like void m(std::initializer_list l) is itself banned under my proposed rules because the only place you should be writing "initializer_list" is in a class' constructor/assignment.

Probably making more explicit where-it's-allowed is understandable/enforceable, if anything is. I liked Nico's and Jeremy's suggestions:
* Can be used to initialize standard containers.

This is what I was trying to allow, except without the word "standard".  It's legal to create a custom container and init it with an initializer list.  All other ways of writing new code that takes an initializer list are banned.

But if that's confusing, I'm fine with making the rule for now be "initializing standard containers".
 
* Can be used for hard-coded range-based for loops.

I think this use is OK too, but it seems rarely useful in practice.

Daniel's point about std::vector<int> v{5} being different from std::vector<int> v(5) is interesting, and probably going to confuse people. 

I don't actually think this is too weird, especially with uniform initialization syntax banned, because people won't be using {5} to pass to a non-initializer-list constructor anywhere.  Even when uniform initialization is allowed, I don't foresee a mass switch to {} for all construction.  The only way I'd imagine people will use this beyond initializer_list is for aggregates.  Thus "{5}" still looks like an initializer list.

If we all get used to f{a,b} always being an initializer_list (based on our restricted allowed usage), and then start using it for types other than standard containers (that don't have implicit initializer_list constructors), it's going to throw off the simpler intuitions people build around the syntax.

I don't agree (see above).

All in all, I really don't think that a narrow rule to allow initing STL containers is dangerous or hard to understand, or raises risks that mean we should wait until MSVC 2015.

PK

Jeffrey Yasskin

غير مقروءة،
17‏/12‏/2015، 8:26:20 م17‏/12‏/2015
إلى Peter Kasting،Dana Jansens،Vladimir Levin،Sam McNally،Nico Weber،cxx
I think it's probably safe to say it's ok to use a brace initializer to initialize an STL or user-defined container with a list of elements, as long as you can see the type name next to the brace initializer.

Ok:
  • std::vector<const char*> v = {"a", "b", "c"};
  • std::vector<const char*> v{"a", "b", "c"};
  • WTF::HashSet<int> s = {1, 2, 3};
  • func(std::vector<std::string>{"x", "y"});
Not ok:
  • std::vector<const char*> v = {3, "a"};  // Algorithmic constructor.
  • func({"x", "y"});  // Even if func() takes a std::vector<std::string>.
Anyone see a case I'd allow that would look similar to something MSVC2013 mis-compiles?

Jeffrey

Scott Hess

غير مقروءة،
18‏/12‏/2015، 9:52:36 ص18‏/12‏/2015
إلى Jeffrey Yasskin،Dana Jansens،Peter Kasting،Vladimir Levin،Sam McNally،Nico Weber،cxx
On Thu, Dec 17, 2015 at 4:51 PM, Jeffrey Yasskin <jyas...@chromium.org> wrote:
This looks a lot like the MSVC miscompile in https://codereview.chromium.org/1130873007/#msg13. That makes me nervous about allowing any of these until we go to MSVC 2015. At most, an initialization of a class-whose-name-makes-it-clearly-a-container could make sense.

At this point, this thread is long enough and contains enough input from pretty expert C++ programmers that I don't see how anyone can argue that the point isn't at least somewhat confusing, and we've lived without it for many years, and it will be resolved in less than a year.  So worst case it doesn't happen and we'll get there presently.

Maybe it would be easier for someone to "just" write a clang plug-in to only accept the known-good cases, and then hope that the trybots have enough coverage for clang on Windows code to make it safe?  I don't know if that idea is even practical given the current systems, but if it is it might be quicker to accomplish than spending a few dozen hours arguing the point.

-scott

Nico Weber

غير مقروءة،
07‏/01‏/2016، 4:54:18 م7‏/1‏/2016
إلى Scott Hess،Jeffrey Yasskin،Dana Jansens،Peter Kasting،Vladimir Levin،Sam McNally،cxx
styleguide/c++/OWNERS discussed this today. Decision: Wait for 2015 availability (should be relatively soon), then discuss more. We might want to write something similar to our rvalue reference doc about the different kinds of initializations and their pitfalls. For now, move to banned.
الرد على الكل
رد على الكاتب
إعادة توجيه
0 رسالة جديدة