0

I am learning a physics library and it involves creating info structs and then passing them to initialiser functions:

btRigidBodyConstructionInfo rbInfo = (/*values...*/);
btRigidBody* rigidBody = new btRigidBody(rbInfo);

Now I want to reset the struct (null it) to reuse it. I found another question on SO which is identical, and the answer was either to create a zero-initialised temporary and set it to it, or after C++11:

rbInfo = {};

When I tried this I got the message:

"No operator "=" matches these operands.

I was confused and but then found out that this syntax attempts to call the default constructor (at least in my compiler (Visual Studio 2017)), and rbInfo doesn't haven't any. I have a feeling that that syntax does the same as:

rbInfo = btRigidBodyConstructionInfo{};

In this case is there no way to reset the struct without setting the individual members? I thought about memsetting, but that's a bad idea generally because there might be things like vtable pointers and such, right?

Quentin
  • 62,093
  • 7
  • 131
  • 191
Zebrafish
  • 11,682
  • 3
  • 43
  • 119
  • `... rbInfo = (...);` is unlikely to be any sort of correct. How are you really initializing it? – StoryTeller - Unslander Monica Jan 04 '18 at 09:46
  • 3
    If your class has a sensible "empty" state, why not implement a default constructor? – Quentin Jan 04 '18 at 09:47
  • @Quentin The class is from the Bullet physics library, not mine. – Zebrafish Jan 04 '18 at 09:47
  • You can maybe red [this](https://stackoverflow.com/questions/3030829/c-is-it-possible-to-set-an-object-to-null) to get some ideas. – apalomer Jan 04 '18 at 09:49
  • `rbInfo = {};` is not correct syntax, while `rbInfo = btRigidBodyConstructionInfo{};` will create a temporary object, fill it with zeroes (since `rbInfo` does not have default constructor) and then assign to `rbInfo`. – user7860670 Jan 04 '18 at 09:49
  • @VTT I did test it, and it does seem to work, and then if I make my default constructor unavailable it throws errors. I'm not sure if that answer is incorrect, it is here: https://stackoverflow.com/questions/15183429/c-completely-erase-or-reset-all-values-of-a-struct – Zebrafish Jan 04 '18 at 09:50
  • 2
    @VTT I'm pretty sure it's correct syntax for implicitly-default-constructible objects. – Quentin Jan 04 '18 at 09:50
  • You seem to mix copy initialization syntax `btRigidBodyConstructionInfo rbInfo = {};` which is valid with copy assignment syntax `rbInfo = {};` which is not valid. Don't let presence of `=` confuse you. – user7860670 Jan 04 '18 at 09:52
  • The crux of the matter is that you are using MSVC. In any other modern compiler, `rbInfo = {};` should work so long as the default c'tor is not explicit. – StoryTeller - Unslander Monica Jan 04 '18 at 09:53
  • 2
    @VTT [I'm not](http://coliru.stacked-crooked.com/a/b83917bc611476c0) ;) – Quentin Jan 04 '18 at 09:53
  • @StoryTeller what a cheap jab at MSVC :p -- but the issue here is that `btRigidBodyConstructionInfo` [does not have a default constructor at all](http://bulletphysics.com/Bullet/BulletFull/structbtRigidBody_1_1btRigidBodyConstructionInfo.html)... – Quentin Jan 04 '18 at 09:54
  • Alright then, I guess one need to always mark default constructors as `explicit` then... – user7860670 Jan 04 '18 at 09:55
  • @StoryTeller Sorry maybe I wasn't clear, the only constructor is one that takes arguments so I assume there is no default constructor. I'm wondering whether there's a way to reset the struct in any way, as neither rbInfo = {}; nor rbInfo = btRigidBodyConstructionInfo{}; works. – Zebrafish Jan 04 '18 at 09:55
  • @Quentin - There has to be one to support value initialization. Even for aggregates. And I'ma jab at MSVC until it hurts :P – StoryTeller - Unslander Monica Jan 04 '18 at 09:56
  • @Quentin Yes, I'm sorry if that wasn't clear, no default constructor. On the topic of MSVC, I've often come here with problems only to find that it doesn't conform to some standard... – Zebrafish Jan 04 '18 at 09:57
  • @Zebrafish since this class does not provide a default constructor, either it is not meant to have a "default" state, or it is poorly implemented. What use do you make of that "empty" instance? Maybe you should change *that* to not need it? – Quentin Jan 04 '18 at 09:57
  • @Quentin - And now I entered the link and understood your point. Yes, not default c'tor. This takes me back to my first comment on this post. No way `... rbInfo = (...);` is any sort of correct. – StoryTeller - Unslander Monica Jan 04 '18 at 09:58
  • @Quentin Well I'm just starting out with this library, so I'm figuring out things as I go along. But I guess the answer to my question is that if there's no default constructor it can't be reset in any quick way like {} or something. – Zebrafish Jan 04 '18 at 09:59
  • @Zebrafish - If there's no default c'tor, there's no "default state" for you to put the object in. `{}` is not magic. – StoryTeller - Unslander Monica Jan 04 '18 at 09:59
  • 2
    If there is only a constructor with parameters, how did you create the "empty" structure? –  Jan 04 '18 at 10:00
  • 3
    If your class doesn't have a default constructor what do you mean by "reset"? To reset anything, there has to be a specification of what the end state is - and, practically, that will usually mean the same state as a default-constructed object. If there is no default constructor, you need to specify the required end state, – Peter Jan 04 '18 at 10:01
  • 1
    @StoryTeller I thought I was wanting to put it in a zero state, not in a default state. But I think you're right. It has values passed to it like mass, where if mass is zero then it's not calculated dynamically, so I wanted to start at null, but it's no big deal. I was curious if there was a way to do this with C++, doesn't seem there is without a default constructor. – Zebrafish Jan 04 '18 at 10:02
  • 4
    @Zebrafish - Yes, there is no safe way, by design. If a type needs to be initialized a certain way, it's usually for a reason. The language itself won't allow you to default initialize something unless it supports it, either explicitly or implicitly. – StoryTeller - Unslander Monica Jan 04 '18 at 10:04
  • Hi, Is this the header for rigid body? https://github.com/bulletphysics/bullet3/blob/master/src/BulletDynamics/Dynamics/btRigidBody.h If so I don't think there's a safe way to do what you want and the answer will be create a new object. I'll explain in an answer if that is the header. – Persixty Jan 04 '18 at 10:26

2 Answers2

4

Your question sounds a bit like a small XY problem.

You state your goal is

Now I want to reset the struct (null it) to reuse it.

The X is "reuse the structure," but somehow a Y of "reset the structure" got mixed in. If all you want to do is reuse the structure, you can just do this:

btRigidBodyConstructionInfo rbInfo = (/*values...*/);
btRigidBody* rigidBody = new btRigidBody(rbInfo);

rbInfo = {/*new values...*/};

Or, if your compiler is too old to support that syntax, then:

rbInfo = btRigidBodyConstructionInfo(/*new values...*/);
Angew is no longer proud of SO
  • 167,307
  • 17
  • 350
  • 455
  • rbInfo = {/* new values...*/}; seems to be easiest. Yes, I get what you mean, but I was curious about whether the language had any syntax to null out a struct that's already been created, as there seems to be a lot of hidden little features in this language, and also things are added every couple of years and I'm always late to find out. – Zebrafish Jan 04 '18 at 10:13
  • @Zebrafish it does, but *only if* that is meaningful for that type. Which is defined by the author of the type, by providing a default constructor and an assignment operator (either or both of which *may* be implicit) – Caleth Jan 04 '18 at 10:38
  • @Caleth What you're saying is that if the author had any intention of having the struct reset to null values he would have made a default constructor to null all the values? A default constructor doesn't necessarily null the values. If we want to null the values on initialisation you do Foo foo{};, which isn't the default one, I don't think. – Zebrafish Jan 04 '18 at 10:44
  • 1
    @Zebrafish A constructor is "the default constructor" if it has no parameters. `Foo foo{};` *must* call `Foo`'s default constructor, because it supplies no parameters – Caleth Jan 04 '18 at 10:51
  • 2
    @Zebrafish Nulling values (as in literally zeroing them) of a class object's members makes little sense in C++ in the general case. One of the main features of classes is supporting *invariants* (always having the class in some defined, consistent state). Explicitly zeroing out would go *clearly* against that. – Angew is no longer proud of SO Jan 04 '18 at 10:52
  • @Caleth Then there can be more than one default constructor if I call Foo{} and then Foo(), right? – Zebrafish Jan 04 '18 at 12:06
  • @Zebrafish both expressions `Foo{}` and `Foo()` create a temporary `Foo` using `Foo::Foo()`. Generally you then do something with that `Foo` before the end of the enclosing statement (like copying it into an existing `Foo` variable), where it ceases to exist (with `Foo::~Foo()` being called) – Caleth Jan 04 '18 at 12:38
  • @Caleth Right, but this idea of "the default constructor" implying there is only one is not clear, as Foo{} and Foo() both call the default constructor but do entirely different things, the first one being zero-initialised. – Zebrafish Jan 04 '18 at 12:47
  • 1
    @Zebrafish No, they do not do entirely different things; in fact, they do the exact same thing. `()` and `{}`, when valid as initialisers, both do *value initialisation:* for built-in types, this means zero-initialisation. For classes, this means calling the default ctor. – Angew is no longer proud of SO Jan 04 '18 at 12:51
  • 1
    You might be confused by aggregate initialization, which is a special case of [list initialization](http://en.cppreference.com/w/cpp/language/list_initialization) with the form `Foo foo {}` (or `Foo foo = {}`), however that is a **statement**, not an expression – Caleth Jan 04 '18 at 12:56
  • @Caleth There's no doubt I'm so confused. However what I should have said is that if the default constructor is compiler-provided, that Foo foo; and Foo foo{}; both call the default constructor and they do different things, Foo foo{} will zero-initialise. "the default constructor" doesn't seem to be just one in this case. – Zebrafish Jan 04 '18 at 13:33
  • @Zebrafish The difference is that `Foo foo;` does default-initialisation, while `Foo foo{};` does value-initialisation. IOW, the constructor is the same both times; but the other stuff that happens as part of the initialisation declaration/expression can differ. – Angew is no longer proud of SO Jan 04 '18 at 13:43
1

In C++ variables have values, not references, backing them. This is distinct from most other languages, where variables are references to possible values.

Some languages have simple data be actual values, while classes are always references. Few languages have class objects whose variables are actually the value of the class.

There is no generic "null" state for (all kinds of) values. Values, in general, are not "nullable". In C++ you can enforce that a type never holds byte values of 0. For example:

class Foo {
  Foo(){}
private:
  int x = -1;
};

this really simple class can never have its x value be 0.

For some types of values, you can literally zero them out (set all their bytes to zero). For other types of values, this is explicitly not allowed.

A concept similar to being nullable is being constructible from zero arguments. If that is the case, and you have a copy or move assign operator, then

foo = {};

will (usually) assign to foo a temporary zero-argument constructed Foo (assuming foo is of type Foo, and sane operator= overloads).

If your type btRigidBodyConstructionInfo has no zero-argument constructor, and is not an aggregate, then you cannot do foo = {}; if foo is btRigidBodyConstructionInfo foo;.

You can do foo = {construction arguments};, and assuming a sane operator= it will do what you want.

If you want a nullable value type, consider boost::optional or std::optional.

std::optional<btRigidBodyConstructionInfo> foo{ std::in_place, /*values...*/ };

then use *foo at point of use. Later, you can null the value by doing foo={};.

To repopulate it, you can either use

foo = {std::in_place, /*values...*/};

or

foo.emplace( /*values...*/ );

you can test if the foo is nulled by doing if (!foo), and you can access the value (undefined behavior if it is null) by *foo.

This behaves a bit like a pointer, but it is actually a value type.

Yakk - Adam Nevraumont
  • 262,606
  • 27
  • 330
  • 524