C++11 Feature Proposal: (Uniform) Initialization Syntax

751 views
Skip to first unread message

Alex Vakulenko

unread,
Sep 24, 2014, 6:18:32 PM9/24/14
to chromium-dev
What:
Using "{}" for initializing classes/members/variables. Leave "()" for function calls.

Why:
1. It's just nice and "uniform":

struct MyPOD {
  int a;
  int b;
};

class MyClass {
 public:
  MyClass(int a, int b) : a_(a), b_(b) {}
 private:
  int a_;
  int b_;
};

void foo() {
  MyPOD pod{1, 2};
  MyClass cls{1, 2};
  int int_val{1};
}

2. It allows value initialization of classes using their default constructors:

class MyClass {
 public:
  MyClass() : a_(0) {}
  MyClass(int a) : a_(a) {}
 private:
  int a_;
};

void foo() {
  MyClass c1{};  // default
  MyClass c2{2};
}

void bar() {
  MyClass c1();  // function declaration ("c1" takes no args, returns MyClass).
  MyClass c2(2);
}

3. It is a bit safer in some situations. C++11 adds a requirement that narrowing conversions are not allowed with {}-style initialization.

Example:

  int a = 2.2;  // Ok, assigns 2
  int b(2.2);  // Ok, assigns 2
  int c{2.2};  // Does not compile: "conversion from 'double' to 'int' requires a narrowing conversion"

P.S. There are known drawbacks/side-effects, though. Related to overlap between this feature and initializer lists. If a class provides normal and initializer list constructors, the latter wins, so:

std::vector<int> values(10);  // creates a vector of 10 ints (that are default initialized to 0).
std::vector<int> values{10};  // creates a vector of 1 element (with value of 10).


Where:
I would recommend start using in new code and migrate the old one organically to this style of initialization.

James Robinson

unread,
Sep 24, 2014, 6:23:43 PM9/24/14
to Alex Vakulenko, chromium-dev

P.S. There are known drawbacks/side-effects, though. Related to overlap between this feature and initializer lists. If a class provides normal and initializer list constructors, the latter wins, so:

std::vector<int> values{10};  // creates a vector of 1 element (with value of 10).

Actually, this behaves differently on different toolchains depending on whether the implementation of std::vector<> in use supports initializer lists or not.  In practice that means that the vector will have a different size and contents on different platforms since we use both sorts of standard libraries.  This seems pretty bad to me - bad enough that I think we should hold off until the standard libraries we use have consistent behavior.

- James

Nico Weber

unread,
Sep 24, 2014, 6:44:58 PM9/24/14
to James Robinson, Alex Vakulenko, chromium-dev
I agree, it seems better to explicitly ban this feature (until we have c++ library features) than to allow it.

Victor Khimenko

unread,
Sep 24, 2014, 7:03:01 PM9/24/14
to Nico Weber, James Robinson, Alex Vakulenko, chromium-dev
Could we add a message which clearly explains WHY it's banned? The fact that without support from standard library it leads to problem with vector on some (but not all!) platforms is bad enough that it should clearly be banneed, but WITH library support it's pretty nice feature thus we should probably rethink our decision when standard library will be updated on all platforms.

Nico Weber

unread,
Sep 24, 2014, 7:07:09 PM9/24/14
to Victor Khimenko, James Robinson, Alex Vakulenko, chromium-dev
Certainly! 

Sungmann Cho

unread,
Sep 30, 2014, 8:53:41 PM9/30/14
to chromi...@chromium.org
I think we have one more reason to ban this feature.
If we use list initialization inside member initializer list or non-static data member initializer, Visual Studio 2013 Update 3 produces an error C2797.

Krzysztof Olczyk

unread,
Oct 14, 2014, 9:41:44 AM10/14/14
to Chromium-dev
Due to the reasons stated above, I guess, it is a wise decision to (temporarily) 
ban it.

But just for the sake of completeness / later discussion, I wanted to mention 
an interesting use the uniform initialization has in template meta-programming, 
especially when variadic templates are used. 

Consider the following code:

template <typename T>
int CalculateSomethingWithSideEffect(
    int* side_effect, const T& arg) {
  // ...
}

template <typename... Ts>
void Bar(const Ts&... args) {
  // ...
}

template <typename... Ts>
void Foo(const Ts&... args) {
  int side_effect;
  Bar(CalculateSomethingWithSideEffect(
      &side_effect, args)...);
}

This will not work as it may be expected, as the order in which individual calls 
of CalculateSomethingWithSideEffect are made is not known; compiler is free to 
evaluate expressions passed as function arguments in any order.

However, it is not free to do so for uniform initialization, the standard
requires strict left-to-right evaluation here.
(12.6.1 [class.explicit.init] ¶ 2 and 8.5.4 [dcl.init.list] ¶ 4)

Therefore, as a trick, we can translate Bar function
into a constructor and call it with an initializer list syntax:

template <typename T>
int CalculateSomethingWithSideEffect(
    int* side_effect, const T& arg) {
  // ...
}

struct Bar {
  template <typename... Ts>
  Bar(const Ts&... args) {
    // ...
  }
};

template <typename... Ts>
void Foo(const Ts&... args) {
  int side_effect;
  Bar{CalculateSomethingWithSideEffect(
      &side_effect, args)...};
}

This can be used, for example, to convert gin/function_template.h from pump
to C++11 variadic templates like I did it, for demonstration purposes, 

-- 
Best regards,
Krzysztof Olczyk

Opera Software ASA
TV & Devices

--
--
Chromium Developers mailing list: chromi...@chromium.org
View archives, change email options, or unsubscribe:
http://groups.google.com/a/chromium.org/group/chromium-dev

To unsubscribe from this group and stop receiving emails from it, send an email to chromium-dev...@chromium.org.

Peter Kasting

unread,
Oct 14, 2014, 12:49:22 PM10/14/14
to kol...@opera.com, Chromium-dev
On Tue, Oct 14, 2014 at 6:40 AM, Krzysztof Olczyk <kol...@opera.com> wrote:
This will not work as it may be expected, as the order in which individual calls 
of CalculateSomethingWithSideEffect are made is not known; compiler is free to 
evaluate expressions passed as function arguments in any order.

However, it is not free to do so for uniform initialization, the standard
requires strict left-to-right evaluation here.
(12.6.1 [class.explicit.init] ¶ 2 and 8.5.4 [dcl.init.list] ¶ 4)

Therefore, as a trick, we can translate Bar function
into a constructor and call it with an initializer list syntax:

Why not just call CalculateSomethingWithSideEffect first, then pass its result (stored in a temp) to Bar as a subsequent statement?

When order of operations matters, making it explicit in the code seems far clearer, safer, and more future-proof than relying on obscure details of C++ ordering rules.  How many people are going to actually know that order is undefined in function arguments but well-defined in uniform initialization statements?

PK 

Krzysztof Olczyk

unread,
Oct 14, 2014, 4:23:01 PM10/14/14
to pkas...@chromium.org, Chromium-dev
Yes. I'm, of course, also definitely in favor of explicitly ordering it by evaluating the values and storing them in temp, before calling the aggregate function. 

But, this is not possible with a variadic template. Arguments pack cannot be unpacked to the subsequent statements. It can only unpack to comma-separated list of arguments. 
(Note the elipsis ... operator). 

If we'd had, in Chromium, the C++11 stdlib, we could have tried an approach with std::tuple,
but this would've required using constructor with uniform syntax too, as std::make_tuple would have had the same problem. 
--
--
Chromium Developers mailing list: chromi...@chromium.org
View archives, change email options, or unsubscribe:
http://groups.google.com/a/chromium.org/group/chromium-dev

To unsubscribe from this group and stop receiving emails from it, send an email to chromium-dev...@chromium.org.


--

Nico Weber

unread,
Oct 28, 2014, 12:45:24 AM10/28/14
to sungma...@gmail.com, Chromium-dev
This is now banned, until we have c++11 library support and the msvs bug has been fixed: http://chromium-cpp.appspot.com/

--

Jeffrey Yasskin

unread,
Jun 9, 2015, 10:06:43 PM6/9/15
to chromi...@chromium.org, tha...@chromium.org
I have a use case that I think hasn't been mentioned in this thread yet. https://codereview.chromium.org/1172853004/diff/1/content/browser/bluetooth/bluetooth_dispatcher_host.cc#newcode119 defines a very simple struct:

struct BluetoothDispatcherHost::DiscoverySessionOptions {
  std::vector<BluetoothScanFilter> filters;
  std::vector<BluetoothUUID> optional_services;
};

and creates an instance with "new DiscoverySessionOptions{filters, optional_services}". If I had to avoid the initializer list, I'd need to either write a constructor for DiscoverySessionOptions or create a local variable and assign the fields individually.

The guideline I'd suggest is that it's valid to use "AggregateType{arguments}" to create an object inline, if AggregateType has no (user-defined) constructors and is defined nearby enough that it's easy to validate that fact. I guess the open question is whether "AggregateType{arguments}" looks different enough from "std::vector{arguments}" to be a viable style rule.

I haven't double-checked that my use works in MSVC. If it doesn't, that'd be a good reason to keep it banned.

Thoughts?
Jeffrey

Nico Weber

unread,
Jun 9, 2015, 11:08:35 PM6/9/15
to Jeffrey Yasskin, Chromium-dev
On Tue, Jun 9, 2015 at 7:06 PM, Jeffrey Yasskin <jyas...@chromium.org> wrote:
I have a use case that I think hasn't been mentioned in this thread yet. https://codereview.chromium.org/1172853004/diff/1/content/browser/bluetooth/bluetooth_dispatcher_host.cc#newcode119 defines a very simple struct:

struct BluetoothDispatcherHost::DiscoverySessionOptions {
  std::vector<BluetoothScanFilter> filters;
  std::vector<BluetoothUUID> optional_services;
};

and creates an instance with "new DiscoverySessionOptions{filters, optional_services}". If I had to avoid the initializer list, I'd need to either write a constructor for DiscoverySessionOptions or create a local variable and assign the fields individually.

The guideline I'd suggest is that it's valid to use "AggregateType{arguments}" to create an object inline, if AggregateType has no (user-defined) constructors and is defined nearby enough that it's easy to validate that fact. I guess the open question is whether "AggregateType{arguments}" looks different enough from "std::vector{arguments}" to be a viable style rule.

I haven't double-checked that my use works in MSVC. If it doesn't, that'd be a good reason to keep it banned.

Thoughts?

Uniform initialization for vectors requires vector constructors taking std::initializer_lists. We don't have C++11 library features everywhere yet.

(Also, in MSVS2013 uniform initialization triggers oodles of compiler bugs. It's probably better in 2015, but we're not on that yet either.)

Nico

Dana Jansens

unread,
Jun 10, 2015, 1:48:19 PM6/10/15
to Jeffrey Yasskin, chromium-dev, Nico Weber
On Tue, Jun 9, 2015 at 7:06 PM, Jeffrey Yasskin <jyas...@chromium.org> wrote:
I have a use case that I think hasn't been mentioned in this thread yet. https://codereview.chromium.org/1172853004/diff/1/content/browser/bluetooth/bluetooth_dispatcher_host.cc#newcode119 defines a very simple struct:

struct BluetoothDispatcherHost::DiscoverySessionOptions {
  std::vector<BluetoothScanFilter> filters;
  std::vector<BluetoothUUID> optional_services;
};

and creates an instance with "new DiscoverySessionOptions{filters, optional_services}". If I had to avoid the initializer list, I'd need to either write a constructor for DiscoverySessionOptions or create a local variable and assign the fields individually.

The guideline I'd suggest is that it's valid to use "AggregateType{arguments}" to create an object inline, if AggregateType has no (user-defined) constructors and is defined nearby enough that it's easy to validate that fact. I guess the open question is whether

How do you enforce this over time? Someone adds a user-defined constructor later, they don't know you have used an {}-initializer somewhere. Perhaps if we could enforce with clang-plugin?

I'm used to having to write a constructor for this, and I think they look ugly, but do like that when you add a new field to a constructor it will fail to compile at all callsites that don't add the new field.

Jeffrey Yasskin

unread,
Jun 10, 2015, 2:23:26 PM6/10/15
to Nico Weber, Dana Jansens, Jeffrey Yasskin, Chromium-dev
Yeah, that's why I suggested restricting it to aggregates. We could
also just ban uses of braced initializers on standard types, since
those are the ones that vary by platform.

> (Also, in MSVS2013 uniform initialization triggers oodles of compiler bugs.
> It's probably better in 2015, but we're not on that yet either.)

Mhmm, the ones I saw in this thread were around initializing members
with braced initializers. It's not clear that this applies when the
members are aggregates, but I said to only use braced initializers to
create an object inline to avoid those problems. If there are others,
then sure, we can't use this.

On Wed, Jun 10, 2015 at 10:47 AM, Dana Jansens <dan...@chromium.org> wrote:
> On Tue, Jun 9, 2015 at 7:06 PM, Jeffrey Yasskin <jyas...@chromium.org>
> wrote:
>> The guideline I'd suggest is that it's valid to use
>> "AggregateType{arguments}" to create an object inline, if AggregateType has
>> no (user-defined) constructors and is defined nearby enough that it's easy
>> to validate that fact. I guess the open question is whether
>
>
> How do you enforce this over time? Someone adds a user-defined constructor
> later, they don't know you have used an {}-initializer somewhere. Perhaps if
> we could enforce with clang-plugin?

That is a problem. A clang-plugin could enforce it. Adding a
constructor to a user-defined type is also unlikely to cause the
std::vector problem, since it's defined in our code, in one way.

> I'm used to having to write a constructor for this, and I think they look
> ugly, but do like that when you add a new field to a constructor it will
> fail to compile at all callsites that don't add the new field.

True. On the other hand, if you write:

struct MyStruct {
int field1;
int new_field;
};
MyStruct s;
s.field1 = 42;
// Oops, didn't update for the new field.

You have the same problem. Do/should we require all structs to have
constructors to avoid this?

Jeffrey

Dana Jansens

unread,
Jun 10, 2015, 2:31:37 PM6/10/15
to Jeffrey Yasskin, Nico Weber, Chromium-dev
Ya, I tend to err on always having a constructor for exactly this reason. Or a pattern I've used for structs that I want a default constructor for, is to have a SetAll() method for deferred initialization, and pass everything to that.
 

Jeffrey

Nico Weber

unread,
Jun 11, 2015, 11:44:10 AM6/11/15
to Jeffrey Yasskin, Dana Jansens, Chromium-dev
I think the repro in https://codereview.chromium.org/1130873007/#msg13 is in the "members are aggregates, inline object" category and it's silently miscompiled by msvc.

Daniel Cheng

unread,
Apr 4, 2016, 4:14:11 AM4/4/16
to tha...@chromium.org, Jeffrey Yasskin, Dana Jansens, Chromium-dev
Use of uniform initialization syntax [1][2][3][5][6][7] (and initializer lists [4]) is starting to creep into chromium, since there's really no way of enforcing the ban. My impression is that the main blockers were library support (blocked on VS2013) and silent miscompiles (blocked on earlier versions of VS2015). Both of these blockers should be addressed by VS2015 update 2… so how likely is it VS2015 will stick at this point?

Daniel


--

Nico Weber

unread,
Apr 4, 2016, 10:02:36 AM4/4/16
to Daniel Cheng, Jeffrey Yasskin, Dana Jansens, Chromium-dev
The allocator bug was closed 36h ago (https://bugs.chromium.org/p/chromium/issues/detail?id=481611#c20). I think it's likely to stick by now (there's still a linker crash in incremental mode that we papered over by disabling incremental linking for the one component that triggered in practice), but several fixes landed just last week.

Jeremy Roman

unread,
Apr 4, 2016, 11:32:08 AM4/4/16
to Nico Weber, Daniel Cheng, Jeffrey Yasskin, Dana Jansens, Chromium-dev
For what it's worth, when we are ready to support this (i.e. when MSVC 2015 has permanently stuck), I think we should follow the Google internal guidance, which is:
- recommend following go/totw:88 (Googlers-only, sorry) guidance, which is roughly:

1. Prefer assignment syntax when initializing with a value (primitive, container, struct initialization):

int a = 1;
std::string s = "foo";
std::vector<int> vec = {4, 5};
BarStruct bar = {4, nullptr};

rather than

int a{1};
std::string s{"foo"};
std::vector<int> vec{4, 5};
BarStruct bar{4, nullptr};

2. Use parentheses when the initialization does something more than compose values:

base::AutoLock lock(lock_);

rather than

base::AutoLock lock{lock_};

3. Use {} syntax only if the above don't compile.

4. Don't mix {} and auto.


While I appreciate the appeal of the uniform initialization syntax, I don't see a reason to differ from Google C++ on this, and I think these rules are also a smaller change from our current style as well (the new syntax is used in roughly the places where it enables some new functionality).

Peter Kasting

unread,
Apr 4, 2016, 6:57:57 PM4/4/16
to Jeremy Roman, Nico Weber, Daniel Cheng, Jeffrey Yasskin, Dana Jansens, Chromium-dev
On Mon, Apr 4, 2016 at 8:31 AM, Jeremy Roman <jbr...@chromium.org> wrote:
For what it's worth, when we are ready to support this (i.e. when MSVC 2015 has permanently stuck), I think we should follow the Google internal guidance, which is:
- recommend following go/totw:88 (Googlers-only, sorry) guidance, which is roughly:

Are you saying we should add the recommendations of the second item there to the Chromium style guide?  Or is it sufficient to just propose allowing this and let the Google style guide's text be the explicit rule?

PK

Dana Jansens

unread,
Apr 4, 2016, 7:01:31 PM4/4/16
to Peter Kasting, Jeremy Roman, Nico Weber, Daniel Cheng, Jeffrey Yasskin, Chromium-dev
As the TOTW is not public, it seems like it'd be nice to replicate that guidance in the Chromium C++ DosAndDonts.

Jeremy Roman

unread,
Apr 4, 2016, 7:46:43 PM4/4/16
to Dana Jansens, Peter Kasting, Nico Weber, Daniel Cheng, Jeffrey Yasskin, Chromium-dev
I think we should probably just allow the feature (and thus the Google C++ style guide rule would apply), but I would like to publish the TOTW guidance (or an edited version of it) somewhere, because I think having such rules-of-thumb helps reduce variability in the codebase, makes code easier to read, and provides a starting point for developers unfamiliar with new features like this one. "Chromium C++ Do's and Don'ts" works for me.

Peter Kasting

unread,
Apr 4, 2016, 7:57:35 PM4/4/16
to Daniel Cheng, Nico Weber, Jeffrey Yasskin, Dana Jansens, Chromium-dev
On Mon, Apr 4, 2016 at 1:13 AM, Daniel Cheng <dch...@chromium.org> wrote:
(and initializer lists [4])

It seems like "allow uniform init syntax" implies also allowing "call STL constructors that take a std::initializer_list", right?  So if we want uniform init syntax, we would probably want to at least modify the initializer_list entry to note that calling these is OK.  Or do we just want to combine the two issues together and propose allowing std::initializer_list to the degree the style guide allows it (basically, everywhere) at the same time as this?  They're really separate, but I'm worried about people taking permission for one as permission for the other.

PK

Daniel Cheng

unread,
Apr 4, 2016, 8:10:52 PM4/4/16
to Peter Kasting, Nico Weber, Jeffrey Yasskin, Dana Jansens, Chromium-dev
Yes, it seems to me we should probably just move both of them to the allowed section at the same time (with Jeremy's guidelines posted on C++ dos and don'ts).

Daniel

Peter Kasting

unread,
Apr 11, 2016, 8:00:01 PM4/11/16
to cxx
chromium-dev to BCC; switching to cxx.

On Mon, Apr 4, 2016 at 5:09 PM, Daniel Cheng <dch...@chromium.org> wrote:
Yes, it seems to me we should probably just move both of them to the allowed section at the same time (with Jeremy's guidelines posted on C++ dos and don'ts).

OK, I formally propose that we allow uniform init syntax and usage of std::initializer_list to the degree the Google style guide allows.

I also propose we add guidance to the C++ Dos-And-Donts page about what kind of initialization syntax to use when.  I will be happy to write such guidance based on the TOTW Jeremy linked, as well as an internal mail thread that was then linked by that page which has a compelling argument for why the guidance prefers copy-init to direct-init where possible.

PK 
Reply all
Reply to author
Forward
0 new messages