Uniform initialization of an atomic struct? - c++

struct S
{
int x;
int y;
};
std::atomic<S> asd{{1, 2}}; // what should this be? This doesn't work
Edit: Both {{1, 2}} and ({1, 2}) work in g++, neither work in clang. Is there a workaround for clang?

This is clang bug 18097. Here's a long thread discussing the issue, which seems to be that clang only supports scalar types for T in atomic<T>. The C++11 standard clearly states (§29.5/1) that T can be any trivially copyable type.
Both the usages shown in the question should match this constructor
constexpr atomic(T) noexcept;
The only way I can think of working around this is to default construct atomic<S> and then use atomic::store to initialize the object.
std::atomic<S> asd;
asd.store({1,2});

std::atomic<S> asd({1, 2});
std::atomic<S> has a constructor which takes a value of type S.
The initializer list {1, 2} is implicitly converted to a temporary S because of this constructor.

Related

Initialization of an array of non-moveable objects: why does such code fail to compile on GCC?

Here is the code example where Test is a non-copyable and non-moveable class with some virtual members and a user-defined constructor, and B is a class that contains a raw (C-style) array of Test objects:
class Test
{
public:
Test() = delete;
Test(const Test&) = delete;
Test(Test&&) = delete;
Test& operator=(const Test&) = delete;
Test& operator=(Test&&) = delete;
Test(int a, int b) : a_(a), b_(b) {}
virtual ~Test() {}
int a_;
int b_;
};
//----------------
class B
{
public:
/*(1)*/ B() : test_{{1, 2}, {3, 4}} {} // Does not compile on GCC, but compiles on Clang and MSVC
private:
Test test_[2];
};
//----------------
int main()
{
B b;
/*(2)*/ Test test[2] = {{1, 2}, {3, 4}}; // Successfully compiles on GCC, Clang and MSVC
}
I want to initialize B's internal array test_ using the braced initialization syntax (line /*1*/), so that each of the two Test objects would be constructed in-place, without needing to create a temporary and then move it.
On Clang and MSVC, this code compiles without warnings.
But GCC's behavior confuses me: it fails to compile the line /*1*/, while successfully compiling the line /*2*/, where I am using the same syntax to initialize a local array. Yet for compiling the first line, it still requires the deleted move constructor of class Test.
The question is, why? Does the C++ standard clearly define whether these lines /*1*/ and /*2*/ should compile at all? If it does, which of the compilers is right from the standard's point of view? Can this inconsistent behavior be called a GCC bug, or do Clang and MSVC overlook some checks that they are supposed to perform?
I can understand that GCC might require a move constructor in order to create a temporary Test object from the inner braces ({1, 2}), and then move that object into an array. Hence the compilation error. But if that is so, why does it not fail on the line /*(2)*/ for the very same reason? This is the most confusing thing to me in this example.
By the way, here's an interesting observation: if I replace the definition of test_ with std::array<Test, 2> (instead of a "C-style" array), and replace the code in the constructor's initialization list with test_{{{1, 2}, {3, 4}}}, everything starts to compile successfully on all three mentioned compilers.
It is also unclear to me why does GCC not fail in this case on any line, while failing with a "raw" array.
Can anyone explain this as well?
I see no problem with the initialization, so I think this is a GCC bug.
The initialization involved is list-initialization, so we consult [dcl.init.list]/3:
List-initialization of an object or reference of type T is defined
as follows:
[...]
(3.3) Otherwise, if T is an aggregate, aggregate initialization is
performed.
[...]
(An array is an aggregate.) Now we go to [dcl.init.aggr]/3:
When an aggregate is initialized by an initializer list as specified
in [dcl.init.list], the elements of the initializer list are taken as
initializers for the elements of the aggregate, in order. Each
element is copy-initialized from the corresponding
initializer-clause. If the initializer-clause is an expression and a narrowing conversion is required to convert the expression, the
program is ill-formed.
So, for either of the two elements, we are effectively doing Test a = {1, 2}, which is valid because Test(int, int) is not explicit. Therefore, the initialization is well-formed and should be accepted by the compiler.

Brace (aggregate) initialization for structs with default values

Initializing a struct with default values is trivial:
struct X { int a; int b = 2; };
and initializing a struct with a brace initializer is trivial too:
X x = {1, 3};
Suprisingly the init code won't compile, until I remove the default value. So, how would I do the init in such a case? I'd like to keep X a POD without c-tor.
Here is some documentation relevant to the problem:
http://en.cppreference.com/w/cpp/language/aggregate_initialization
In c++11 your code is invalid. In c++14 it is valid again.
In C++11 adding a default initialization prevents braced init from being valid. In C++14, it does not.
A way to solve your problem in C++11 would be to write a constructor with the value for a and the b value with a default.

Initialize an std::array of tuples with curly braces

This probably has a very simple answer, but I really can't figure it out. Why do I get errors for doing this? What's the correct way to initialize something like this?
std::array<std::tuple<int, std::string>, 3> tuples{
{3, "a"},
{7, "b"},
{2, "c"}
};
On MSVC 2015, I get the following errors:
No suitable constructor exists to convert from "int" to "std::tuple<int, std::string>"
No suitable constructor exists to convert from "const char[2]" to "std::tuple<int, std::string>"
This is an outstanding issue with tuple. See, its constructor in C++11/14 is explicit. And therefore, it cannot participate in copy-list-initialization, which is what the inner braced-init-lists do (the outer ones are direct-list-initialization).
The idea was to prevent you from being able to bypass a class's explicit constructors through tuple. But, in C++17, this will be changed: if all of the tuple's types themselves are implicitly convertible from the respective given type, then so too will that constructor of tuple.
For your specific use case, you could use std::pair. Its constructor is never explicit.

Why can I initialize a regular array from {}, but not a std::array

This works:
int arr[10] = {};
All elements of arr are value-initialized to zero.
Why doesn't this work:
std::array<int, 10> arr({});
I get the following warning from g++ (version 4.8.2):
warning: missing initializer for member ‘std::array<int, 10ul>::_M_elems’
There are two issues one which is a matter of style and the warning.
Although it may not be obvious, aggregate initialization is happening on a temporary which is then being used as an argument to the copy constructor. The more idiomatic to do this initialization would be as follows:
std::array<int, 10> arr = {};
Although this still leaves the warning.
The warning is covered by gcc bug report: - -Wmissing-field-initializers relaxation request and one of the comments says:
[...]Surely, the C++ syntax of saying MyType x = {}; should be supported,
as seen here:
http://en.cppreference.com/w/cpp/language/aggregate_initialization
where for instance:
struct S {
int a;
float b;
std::string str;
};
S s = {}; // identical to S s = {0, 0.0, std::string};
That shouldn't warn for the reasons stated in earlier comments.
and a follow-up comment says:
My statement about zero-initialization was inaccurate (thanks), but
the general point still stands: in C you have to write ' = {0}' since
empty-braces initializer is not supported by the language (you get a
warning with -pedantic); in C++, you can write ' = {}' or 'T foo =
T();', but you don't need to write ' = {0}' specifically.
The latest versions of gcc does not produce this warning for this case, see it live working with gcc 5.1.
We can see this topic also covered in the Clang Developers lists in the thead: -Wmissing-field-initializers.
For reference the draft C++11 standard section 8.5.1 [dcl.init.aggr] says:
If there are fewer initializer-clauses in the list than there are
members in the aggregate, then each member not explicitly initialized
shall be initialized from an empty initializer list (8.5.4). [
Example:
struct S { int a; const char* b; int c; };
S ss = { 1, "asdf" };
initializes ss.a with 1, ss.b with "asdf", and ss.c with the value of
an expression of the form int(), that is, 0. —end example ]
So as this is valid C++, although as noted using {} is not valid C99. One could argue that it is only a warning, but this seems like idiomatic C++ using {} for aggregate initialization and is problematic if we are using -Werror to turn warnings into errors.
Firstly, you can use the ({}) initializer with an std::array object, but semantically that stands for direct-initialization using copy-constructor from a temporary value-initialized std::array object, i.e. it is equivalent to
std::array<int, 10> arr(std::array<int, 10>{});
And it is actually supposed to compile.
Secondly, you don't really have to go the ({}) way when you can just do
std::array<int, 10> arr = {};
or
std::array<int, 10> arr{};
The first of the two is the most syntactically similar to your int arr[10] = {};, which makes me wonder why you didn't try it at first. Why did you decide to use ({}) instead of = {} when you built the std::array version of = {} syntax?
Enough people have pointed this out as a "problem" when compiling with -Werror that I think it's worth mentioning that the problem goes away if you just double up:
std::array<int, 10> arr{{}};
Does not produce any warning for me on gcc 4.9.2.
To add a bit as to why this solves it: my understanding was that std::array is actually a class with a C array as its only member. So double up on the braces makes sense: the outer braces indicate you are initializing the class, and then the inner braces default initialize the one and only member of the class.
Since there is no possible ambiguity when there is only one variable in a class, just using one pair of {} should be reasonable, but gcc is overly pedantic here, and warns.

Is initializing with "var{args}" a new feature of C++0x, or merely syntactic sugar?

I was reading the C++0x faq and came across the section detailing initializer lists. The examples were mostly variations of:
vector<int> vi = { 1, 2, 3 };
vector<int> vj({1, 2, 3});
// etc.
However, also listed was the form:
vector<int> vk{2};
This form appears elsewhere in the faq, and I am curious as to whether it is semantically different from the initial two forms, or just syntactic sugar for vk({x, y, z}).
The ({1, 2, 3}) form calls the constructors of vector<int> directly, and passes as first argument a {1, 2, 3}. You could have passed more arguments
vector<int> vk({1, 2, 3}, myAllocator);
If vector<int> would not have a constructor whose first parameter is an initializer_list or of another type that could be initialized by {1, 2, 3} (like, another container class), it would not work. In your case it works because vector<int> in fact has a constructor whose first parameter is a initializer_list<int>. This is just like in normal function calls
void f(vector<int> const& vk);
int main() { f({1, 2, 3}); }
If you omit the parentheses, as in vector<int> vk{1, 2, 3}, the exact meaning depends on the class. A vector<int> has an initializer list constructor, which is a constructor with a first parameter of type initializer_list<int> (optionally a reference to it), and all other params with default arguments. If the class has such a constructor, then the initializer list is passed to that constructor. Alternatively the class could simply be an aggregate (like struct A { int a; int b; int c; };, the initializer list would then init the members) or have a constructor that accepts 3 separate int arguments.
Finally the = { 1, 2, 3 } form is almost identical to the version omitting the parentheses (i.e just removing =), except that it forbids to use explicit constructors (i.e had they declared it as explicit vector(initializer_list<int>); or had they declared a explicit vector(int, int, int); instead, it would result in an error if you use = { 1, 2, 3 }).
One is uniform initialization, and the other is initializer lists. They are two different things, although as you can see, they can produce similar syntax.
vector<int> vk{2};
is a uniform initialization- the other two are initializer lists.
The uniform initialization prevents narrowing conversions i.e. conversions that would cause loss of data:
#include <vector>
std::vector<float> v{1.0F, 2.0F, 3.0F}; // OK:
std::vector<float> w{1.0, 2.0, 3.0}; // OK: doubles could be put into floats without loss.
std::vector<int> j{1.1, 2.2, 3.3}; // error: narrowing
std::vector<int> k{1L, 2L, 3L}; // OK: the long numbers can be represented as int without loss.
std::vector<int> l{0xfacebeefL, 0xdeadbabeL, 0xfadecabeL}; // error: narrowing.