Anonymous (?) initialization of a struct passed as an argument in C++03 - c++

Say, I have
struct Foo
{
char a;
char b;
};
void bar(Foo foo);
What's the most succinct way to initialize a struct and pass it to the function? Ideally I would like to write something like
bar(Foo = {'a','b'});
What if Foo was a union?
UPD: My sincere apologies, the question was supposed to be in relation to C++03 only. Also, in this particular case, going away from POD is to be avoided (the code is for embedded system, ergo shorter bytecode is sought after). vonbrand, thanks for the C++11 answer.

In C++ 11 you can write:
bar({'a', 'b'});
or:
bar(Foo{'a', 'b'});
(see Stroustup's C++11 FAQ).
g++-4.8.2 accepts this without complaints only if you give it -std=c++11, clang++-3.3 gives an error unless -std=c++11

You could add a constructor to your struct. e.g.
struct Foo
{
Foo(char a, char b) : a(a), b(b) {}
char a;
char b;
};
Then you could call your function
bar(Foo('a', 'b'));
If it was a union, you could have different constructors for the different types of the union.

Related

Why can't I call operator() on temporary objects directly?

What I want to do can be summarized into the following code:
struct A{};
struct B{
A& a;
B(A& a) noexcept : a(a){}
int operator()(int) {}
};
int main(){
A a;
B(a)(2);
}
And my compiler (g++ 6) rejected the code complaining that a shadows a parameter. However, if I try to explicitly call operator(), it works as expected.
It seems that g++ will ignore the parentheses and see the statement as a declaration.
Is this the specified or expected behavior?
This is one of those icky parsing rules which catches you every now and again. As you suggest, B(a)(2); is actually equivalent to B a(2);, so your code tries to initialize a B with an int.
To fix this, you can use C++11's uniform initialization:
B{a}(2);

Aligned storage and standard layout

Consider the following C++11 code:
#include <type_traits>
struct bar
{
virtual void do_bar() const {}
};
struct foo
{
std::aligned_storage<sizeof(bar),alignof(bar)>::type m_storage;
};
bar is not standard layout because of the virtual function do_bar(). However, foo is standard layout as the type provided by std::aligned_storage is a POD type and foo satisfies all the other requirements for standard layout types.
What happens then when I use the m_storage storage with placement new to construct an instance of bar? E.g.,
foo f;
::new(static_cast<void *>(&f.m_storage)) bar();
Is this legal? Can I use this to cheat my way around restrictions about standard layout types?
Here's your code again:
struct bar {
virtual void do_bar() const {}
};
struct foo {
std::aligned_storage<sizeof(bar), alignof(bar)>::type m_storage;
};
This is fine. struct foo is a standard-layout type, and, given an instance foo myFoo, you can construct an object of type bar into myFoo.m_storage.
However, this is completely pointless from the compiler's POV, so why bother with it? As #dyp wisely said in the comments, "Why do you want foo to be standard-layout?"
You handwaved something about unions. Well, that's fine. You can write this:
union DoesntWork {
bar b; // compiler error in C++11 due to non-standard-layout type
int i;
};
union DoesWork {
foo f; // works fine in C++11, of course
int i;
};
However, equally obviously, you cannot expect this to work:
struct car {
int initialsequence;
};
struct bar {
int initialsequence;
virtual void do_bar() const {}
};
struct foo {
std::aligned_storage<sizeof(bar), alignof(bar)>::type m_storage;
bar& asBar() { return *reinterpret_cast<bar*>(&m_storage); }
};
union JustSilly {
foo f;
car c;
} js;
assert(&js.c.initialsequence == // Fails, because no matter how many
&js.f.asBar().initialsequence); // casts you add, bar still has a vptr!
In other words, you're free to lie to the compiler (via type-punning and reinterpret_cast), but that doesn't make your lies true. ;)
See also: XY problem.
Tried in OSX's XCode C++11 compiler option, and seems to work for me. Of course, you probably want to do "::new(static_cast(&f.m_storage)) bar();" in foo's constructor and invoke its destructor in foo's destructor.
When working with aligned quantities
1) It is advisable to specify alignment for the class or struct using declspec(align(16)) or __attribute((aligned(16))). I have encounter some errors when enabling optimization using VS2010, when I didn't do this.
2) I usually avoid overloading new and use the placement operator like you suggest, e.g.
#include <new> // Remember this otherwise the placement operator is not defined
SomeClass* c = (SomeClass*) _mm_malloc(sizeof(SomeClass),16);
new c SomeClass(); // This is perfectly legal
// Some work
_mm_free(c);
3) A good rules of thumb is to place aligned quantities at the start of your struct or class. This way the compiler won't do zero-padding in between members and warn about this.

Constructor with default values & Different Constructors

I would like to do something like this
class foo{
private:
double a,b;
public:
foo(double a=1, double b=2){
this.a=a;
this.b=b;
}
}
int main(){
foo first(a=1);
foo first(b=2);
}
Is such thing possible or do I need to create two new constructors?
Here comes the 2. question: What is the difference in those two constructors:
class foo{
private:
int a;
public:
foo(int in):a(in){}
}
or
class foo{
private:
int a;
public:
foo(int in){a=in}
}
foo first(a=1);
foo first(b=2);
You cannot really have this in C++. It was once considered for standarization but then dropped. Boost.Parameter does as much as it can to approximate named parameters, see
http://www.boost.org/doc/libs/1_47_0/libs/parameter/doc/html/index.html
foo(int in):a(in){}
foo(int in){a=in}
The first constructor is initializating a, while the second is assigning to it. For this particular case (int) there isn't much of a difference.
In C++, the this pointer is a pointer not an object (in java, you would access it with the .). That said, you would need a dereferencing arrow (i.e. this->member or (*this).member).
In any case, this can be done. However, parameterization in C++ works in reverse order and cannot be named. For instance, int test(int a=2, int b=42) is legal as well as int test(int a, int b=42). However, int test(int a=2, b) is not legal.
As for your second question, there is no significant difference between the constructors in this example. There are some minor (potential) speed differences, but they are most-likely negligible in this case. In the first one you are using an initializer list (required for inheritance and calling the constructor of the base class if need be) and the second one is simply explicitly setting the value of your variable (i.e. using operator=()).
It's probably overkill for your example, but you might like to learn about the Named Parameter Idiom.
C++ doesn't support named parameters so this:
int main()
{
foo first(a=1);
foo first(b=2);
}
Is not legal. You also have multiple non-unique idenfiers (e.g. first).

C++ initializing constants and inheritance

I want to initialize constant in child-class, instead of base class. And use it to get rid of dynamic memory allocation (I know array sizes already, and there will be a few child-classes with different constants).
So I try:
class A {
public:
const int x;
A() : x(0) {}
A(int x) : x(x) {}
void f() {
double y[this->x];
}
};
class B : A {
B() : A(2) {}
};
Pretty simple, but compiler says:
error C2057: expected constant expression
How can I say to compiler, that it is really a constant?
It isn't a constant though. It can still be modified by the constructor. Only a compile time constant is allowed for the size of an array. When the compiler says "constant expression", it is not meaning an expression which returns a constant value, but an constant, such as "52" or "45" or something along those lines.
Use std::vector instead.
EDIT: In response to "I know array sizes already, and there will be a few child-classes with different constants"
The only way to do that is to use a template.
template<size_t x>
class A {
public:
void f() {
double y[x];
}
};
typedef A<2> B;
The behaviour you expect could be achieved using the following template.
Note that this is actually unreliable, disgusting and could be used only as "a sample". Use std::vector instead.
template <size_t a = 0>
class A {
public:
A() { }
void f() {
int y[a];
y[0] = 5;
}
};
class B : A<2> {
B() { }
};
void main() {
A<1> a;
a.f();
// Undefined behaviour - creating an array of size 0
// At least, MSVS2008 treats it as an error :)
// A<0> a_;
}
There's "constant", and then there's "constant". If you want to allocate an array on the stack like that, the compiler needs the length of the array at compile time, and based on what you've given there it can't figure that out. Interestingly, gcc supports an extension (not supported in standard C++) that allows for stack allocation for variable lengths.
I don't know if it will work for your purposes, but one possibility would be to make it a template parameter:
template <int size>
class A {
double y[size];
};
In this case, you'd probably want to create an instance of A in B instead of using inheritance.
The other obvious possibility would be to use a tr1::array object instead. This is is also a template, so the idea is pretty much the same, but it's already written, tested and working so you can avoid all that. If your compiler doesn't supply TR1 classes, Boost has a mostly conforming implementation (boost::array).

memset for initialization in C++

memset is sometimes used to initialize data in a constructor like the example below. Does it work in general ? Is it a good idea in general?
class A {
public:
A();
private:
int a;
float f;
char str[35];
long *lp;
};
A::A()
{
memset(this, 0, sizeof(*this));
}
Don't use memset. It's a holdover from C and won't work on non-PODs. Specifically, using it on a derived class that contains any virtual functions -- or any class containing a non-builtin -- will result in disaster.
C++ provides a specific syntax for initialization:
class A {
public:
A();
private:
int a;
float f;
char str[35];
long *lp;
};
A::A()
: a(0), f(0), str(), lp(NULL)
{
}
To be honest, I'm not sure, but memset might also be a bad idea on floating-points since their format is unspecified.
It's a terrible idea. You're just tromping over data, paying no heed to how objects should be initialized. If your class is virtual, you're likely to wipe out the vtable pointer as well.
memset works on raw data, but C++ isn't about raw data. C++ creates abstractions, so if you want to be safe you use those abstractions. Use the initializer list to initialize members.
You can do it to POD types:
struct nothing_fancy_here
{
bool b;
int i;
void* p;
};
nothing_fancy_here x;
memset(&x, 0, sizeof(x));
But if you're doing it on this, that means you're in a user-defined constructor and no longer qualify as a POD type. (Though if all your members are POD it might work, as long as none contain 0 as a trap value. I'm sure not sure if any other sources of undefined behavior come into play here.)