This question already has answers here:
Why does C++ disallow anonymous structs?
(7 answers)
Closed 8 years ago.
How would you go about doing this in standard C++11/14 ? Because if I'm not mistaken this isn't standard compliant code with the anonymous structs.
I wish to access the members the same way as you would with this.
template <typename some_type>
struct vec
{
union {
struct { some_type x, y, z; };
struct { some_type r, g, b; };
some_type elements[3];
};
};
Yes, neither C++11 nor C++14 allow anonymous structs. This answer contains some reasoning why this is the case. You need to name the structs, and they also cannot be defined within the anonymous union.
§9.5/5 [class.union]
... The member-specification of an anonymous union shall only define non-static data members. [ Note: Nested types, anonymous unions, and functions cannot be declared within an anonymous union. —end note ]
So move the struct definitions outside of the union.
template <typename some_type>
struct vec
{
struct xyz { some_type x, y, z; };
struct rgb { some_type r, g, b; };
union {
xyz a;
rgb b;
some_type elements[3];
};
};
Now, we require some_type to be standard-layout because that makes all the members of the anonymous union layout compatible. Here are the requirements for a standard layout type. These are described in section §9/7 of the standard.
Then, from §9.2 [class.mem]
16 Two standard-layout struct (Clause 9) types are layout-compatible if they have the same number of non-static data members and corresponding non-static data members (in declaration order) have layout-compatible types (3.9).
18 If a standard-layout union contains two or more standard-layout structs that share a common initial sequence, and if the standard-layout union object currently contains one of these standard-layout structs, it is permitted to inspect the common initial part of any of them. Two standard-layout structs share a common initial sequence if corresponding members have layout-compatible types and either neither member is a bit-field or both are bit-fields with the same width for a sequence of one or more initial members.
And for the array member, from §3.9/9 [basic.types]
... Scalar types, standard-layout class types (Clause 9), arrays of such types
and cv-qualified versions of these types (3.9.3) are collectively called standard-layout types.
To ensure that some_type is standard layout, add the following within the definition of vec
static_assert(std::is_standard_layout<some_type>::value, "not standard layout");
std::is_standard_layout is defined in the type_traits header. Now all 3 members of your union are standard layout, the two structs and the array are layout compatible, and so the 3 union members share a common initial sequence, which allows you to write and then inspect (read) any members belonging to the common initial sequence (the entire thing in your case).
Anonymous unions are allowed in C++11/14. See the example of their usage at Bjarne Stroustrup's C++11 FAQ
Regarding anonymous structs see Why does C++11 not support anonymous structs, while C11 does? and Why does C++ disallow anonymous structs and unions?
Though most compilers support anonymous structs, if you want your code to be standard compliant you have to write something like this:
template <typename some_type>
struct vec
{
union {
struct { some_type x, y, z; } s1;
struct { some_type r, g, b; } s2;
some_type elements[3];
};
};
I think the other answers sort of missed the point of the question:
I wish to access the members the same way as you would with this.
In other words, the question is really "how do I define a type vec in a standard-compliant manner such that given an object u of that type, u.x, u.r, and u.elements[0] all refer to the same thing?"
Well, if you insist on that syntax...then the obvious answer is: references.
So:
template <typename some_type>
struct vec
{
vec() = default;
vec(const vec& other) : elements{ other.elements[0], other.elements[1], other.elements[2] } {}
vec & operator=(const vec &other) {
elements[0] = other.elements[0];
elements[1] = other.elements[1];
elements[2] = other.elements[2];
return *this;
}
some_type elements[3];
some_type &x = elements[0], &y = elements[1], &z = elements[2];
some_type &r = elements[0], &g = elements[1], &b = elements[2];
};
The first problem with this approach is that you need extra space for 6 reference members - which is rather expensive for such a small struct.
The second problem with this approach is that given const vec<double> v;, v.x is still of type double &, so you could write v.x = 20; and have it compile without warning or error - only to get undefined behavior. Pretty bad.
So, in the alternative, you might consider using accessor functions:
template <typename some_type>
struct vec
{
some_type elements[3];
some_type &x() { return elements[0]; }
const some_type &x() const { return elements[0]; }
some_type &y() { return elements[1]; }
const some_type &y() const { return elements[1]; }
some_type &z() { return elements[2]; }
const some_type &z() const { return elements[2]; }
some_type &r() { return elements[0]; }
const some_type &r() const { return elements[0]; }
some_type &g() { return elements[1]; }
const some_type &g() const { return elements[1]; }
some_type &b() { return elements[2]; }
const some_type &b() const { return elements[2]; }
};
You would have to write u.x() etc. instead of u.x, but the space savings is considerable, you can also rely on the compiler-generated special member functions, it's trivially copyable if some_type is (which enables some optimizations), it's an aggregate and so can use the aggregate initialization syntax, and it's also const-correct.
Demo. Note that sizeof(vec<double>) is 72 for the first version and only 24 for the second.
Related
When declaring a union, e.g.
union u
{
int x;
float y;
};
you can construct the union as follows:
u integer { .x = 10 };
u fraction { .y = 10.f };
Suppose I have a pointer to member,
auto union_member_pointer = &u::y;
What syntax allows me to abstractly assign to that member of the union? i.e.
u unknown { .*union_member_pointer = { } }; // doesn't work
Designated initialization is not permitted to be dynamic. It is applied to the names of members, not of member pointers or anything of the like.
I want to define an aggregate with a number of mutable fields (to save it in std::set or std::priority_queue and modify it in future, surely saving the container invariants). I tried the following syntax and it was compiled successfully:
#include <cstdlib>
int
main()
{
struct X
{
mutable struct
{
int i;
int j;
};
};
X const x{};
//x.i = 1;
return EXIT_SUCCESS;
}
Live example for clang 3.8.
But statement x.i = 1; gives an error:
error: cannot assign to variable 'x' with const-qualified type 'const X'
My intention was to group a plenty of sequential fileds and apply mutable keyword to the group.
Is this syntax wrong? If so, what is an intention of compiler makers to allow such a syntax if any?
ADDITIONAL:
Code:
#include <cstdlib>
int
main()
{
struct X
{
mutable struct
{
int i;
int j;
};
void f() const { i = 1; }
};
X const x{};
//x.i = 1;
x.f();
return EXIT_SUCCESS;
}
gives an error too:
error: cannot assign to non-static data member within const member function 'f'
note: member function 'main()::X::f' is declared const here
void f() const { i = 1; }
The trouble comes from the mix between the non-standard (in C++) usage of anonymous struct together with the mutable. The latter is a storage class specifier that is meant to be used for members and not for types.
Alternative 1: define an intermediate member for your anonymous struct:
You can define a member that will then be mutable, according to the rules of standard C++:
struct X
{
mutable struct
{
int i;
int j;
} y;
};
X const x{};
x.y.i = 1;
Live demo
Alternative 2: make every members in the anonymous struct mutable:
You can otherwise define the members within the struct as being mutable. As the anonymous struct merges these members into the enclosing struct, the mutable property will be passed on:
struct X
{
struct
{
mutable int i;
mutable int j;
};
};
Online demo
What does the standard say ?
The standatad C++ doen't allow anonymous struct. Anonymous struct is a compiler extension for C11 compatibility.
The C++ standard allows however anonymous unions. But it sets restrictions, notably:
9.5/6: A storage class is not allowed in a declaration of an anonymous union in a class scope.
So when compiling the following code:
struct X
{
mutable union
{
int i;
int j;
};
};
the compiler shall and will issue a very specific error:
prog.cpp:11:13: error: a storage class on an anonymous aggregate in class scope is not allowed
mutable union
I think that it is not consistent to allow using a storage class specifier on an anonymous struct (and apparently ignoring it) and to issues an error for an anonymous union. According to me, this shall be reported as a compiler bug. In anycase, you should adopt alternative 1 (portable & compliant) or alternative 2 (compiler dependent, but more consistent with the standard).
Background
What I'm trying to achieve: I'm trying to implement something like a Java enum (ie. enum that has some additional functionality). I came up with a solution using two classes, where one class represents a value and the other acts as an enumeration of possible values using static variables to represent each value. I want it to be a real replacement of enum, including the possibility to use the enum values in template instantiation. For this to work, an enum value needs to be a constant expression (constexpr). However, I'm not sure if I use the constexpr correctly.
The Code
Here is the code that I came up with:
class EnumType {
public:
enum Enum {val_A, val_B, val_C};
friend class EnumTypeList;
EnumType()
: m_ID(val_A), m_foo(0) {}
constexpr operator Enum() const {return m_ID;};
constexpr unsigned int getFoo() const {return m_foo;};
protected:
constexpr EnumType(const Enum v, const int foo)
: m_ID(v), m_foo(foo) {}
private:
Enum m_ID;
int m_foo;
};
class EnumTypeList {
public:
static constexpr EnumType A = EnumType(EnumType::val_A, 5);
static constexpr EnumType B = EnumType(EnumType::val_B, 4);
static constexpr EnumType C = EnumType(EnumType::val_C, 8);
};
The class EnumType holds the information of each value and provides some additional functions (here it is storing the additional value m_foo that can be accessed using getFoo() function). The enumeration itself is represented by the EnumTypeList that contains static constexpr variables, where each variable represents a possible value of an enum. This code allows me to use the variables from EnumTypeList in place of EnumType::Enum. Even in templates like in the following code:
template <EnumType::Enum T>
class Test {
public:
void test() {
std::cout << "Enum is not A" << std::endl;
}
};
template <>
class Test<EnumType::val_A> {
public:
void test() {
std::cout << "Enum is A" << std::endl;
}
};
int main() {
Test<EnumTypeList::A> a;
a.test();
Test<EnumTypeList::B> b;
b.test();
// this shouldn't compile
/*EnumType x = EnumTypeList::C;
Test<x> c;
c.test();*/
}
This works as I would expect – I can use the values from EnumTypeList in place of EnumType::Enum in template instantiation as demonstrated above, but I can't do it with EnumType x = EnumTypeList::C;
The Problem
While the code compiles correctly without any warning under gcc and clang, I'm not sure if I use the constexpr correctly. The thing is that while the EnumType constructor and the conversion operator operator Enum() are constexpr, they both access variables m_ID and m_foo that are not constant (because I need assignment operator).
This is fine, members of literal types are allowed to be modifiable.
In order to use the type in a constant expression you must construct it with constant arguments, but that's OK because you do that;
static constexpr EnumType A = EnumType(EnumType::val_A, 5);
The object constructed is a valid constant expression so can be used to initialize the constexpr variable A. You don't modify the members of the object, so it doesn't matter that they are modifiable.
Clang in particular is very strict about constant expressions, if you were doing something wrong it would give an error.
This object can be used where a constant expression is needed:
constexpr EnumType A5(EnumType::val_A, 5);
e.g.
constexpr int i = A5.getFoo();
This object cannot:
EnumType A6(EnumType::val_A, 6);
constexpr int i = A6.getFoo(); // error
I want to have mechanism that allows me to concatenate variadic function parameters (all of them are convertable into value of some specific plain-old-data type F) into a raw data storage of appropriate size (size is greater than or equal to sum of parameters sizes). I wrote the following code:
#include <iostream>
#include <iterator>
#include <new>
#include <cstdlib>
#include <cassert>
#include <array>
#include <tuple>
template< typename F >
struct repacker
{
constexpr
repacker(F * const _storage)
: storage_(_storage)
{
static_assert(std::is_pod< F >::value, "Underlying type is not a POD type.");
}
F * const storage_;
template< typename... P >
auto operator () (P && ...params) const
{
constexpr auto N = sizeof...(P);
using A = std::array< F, N >; // using A = F [N]; this eliminates the problem
static_assert(sizeof(A) == sizeof(F) * N, "This compiler does not guarantee, that this code to be working.");
#ifndef _NDEBUG
auto a =
#else
std::ignore =
#endif
new (storage_) A{F(params)...};
assert(static_cast< void * >(a) == static_cast< void * >(a->data()));
return N;
}
};
int main()
{
using F = double;
constexpr auto N = 6;
F * a = new F[N];
{
F x(1.0);
F const y(2.0);
repacker< F > r(a);
auto const M = r(x, y, 3.0, 4, 5.0f, 6.0L);
assert(M == N);
}
std::copy(a, a + N, std::ostream_iterator< F const & >(std::cout, " "));
std::cout << std::endl;
delete [] a;
return EXIT_SUCCESS;
}
But I am not sure that the assert(static_cast< void * >(&a) == static_cast< void * >(a.data())); assertion is true for all the compilers. This is a necessary condition for a code to be working.
Is it always the assertion is true?
This is a necessary condition for a code to be working.
No, it is not. OK, it is necessary, but it is not sufficient. Another necessary condition for this (very terrible) code to be working is:
sizeof(std::array<F, N>) == sizeof(F) * N;
And that is not guaranteed by the standard. std::array is not layout-compatible with C-style arrays. The contents of std::array are, but not the full type itself.
If you want to initialize some object in memory you allocate in blocks of bits, you should new up a char[] array, not an array of F. You should allocate this:
char *a = new char[sizeof(std::array<F, N>)];
#AndyProwl pointed out something very important:
std::array is guaranteed to be an aggregate as defined by C++11 8.5.1/1:
An aggregate is an array or a class (Clause 9) with no user-provided constructors (12.1), no brace-or-equalinitializers for non-static data members (9.2), no private or protected non-static data members (Clause 11), no base classes (Clause 10), and no virtual functions (10.3).
Let us test that against C++11 9/7:
A standard-layout class is a class that:
has no non-static data members of type non-standard-layout class (or array of such types) or reference
[...]
Violationg this is easier than one might expect:
struct violator { virtual ~violator () { } };
typedef ::std::array<violator, 2> violated;
Here the type violated would have a data member that is of type array of a non-standard-layout class.
Therefore the guarantee expressed in C++11 9.2/20 (that allows reinterpreting a pointer to the class as one to its first member, and is as far as I can see the only passage in the standard that might make your assumption valid) does not hold in all cases for ::std::array.
Standard layout types guarantee that you can convert a pointer to them to a pointer of their first member using reinterpret_cast. IIRC reinterpret_cast can potentially return a different address than used as input (due to alignment restriction).
Point from ISO C++ Standard: section 9.5, para 4, line 1:
"A union for which objects or pointers are declared is not
an anonymous union."
Example :
struct X {
union {
int i;
double d;
} ;
int f () { return i;}
};
int main() { return 0; }
IAm expecting an error from this example according to the above point
but GCC,SUN compiler CC,EDG,etc are not showing error
iam expecting this error// error : cannot directly access "i"
please ..conform above example program is correct are wrong
This would make the union not anonymous:
struct X {
union {
int i;
double d;
} *p;
int f () { return i;} // !Nyet.
};
Cheers & hth.,
To add to what Alf is saying, the purpose of the anonymous union language in the C++ spec is to allow scoping of the unions members. If you have a named union inside a struct:
struct X {
union {
int i;
double d;
} varname;
};
i is not a member of the struct X. i is a member of varname, which itself is a member of struct X.
However, if the union doesn't have a member variable declared, then i would have to be accessed directly as a member of X. This can only work if the union has no name (no variables are declared with the union definition). Such unions are called "anonymous unions".