B_Dex_Float
B_Dex_Float

Reputation: 122

Is it legal to have a C++ struct member point to another member inside it

Say I have a struct like this. Is it legal C++ to have a member of the struct point to another member inside it? How are these stored?

struct Foo {
    int m1{};
    int m2{};
    int* pint{};
    std::string str{};
    const char* pstr{};
};

I'm setting the members first, and then the pointers. Is this legal?

Foo a {
    .m1 = 10,
    .m2 = 15,
    .str = "Hello, Earth!"
};

a.pint = &a.m1;
a.pstr = a.str.c_str();

I'm setting the pointers first, and then the members. Is this legal?

Foo a {};

a.pint = &a.m1;
a.pstr = a.str.c_str();
a.m1 = 10,
a.m2 = 15,
a.str = "Hello, Earth!"

I checked on Godbolt.org, and it seems to work even at the highest optimization levels, but wanted to make sure it was legal C++ lest the UB kraken devour my program.

Is there a way I can aggregate initialize the pointers inline to the members of the struct? Something like this?

Foo a {
    .m1 = 10,
    .m2 = 15,
    .pint = // address to m2?
    .str = "Hello, Earth!",
    .pstr = // something.c_str()?
};

P.S. How do I phrase this question, looking for struct member pointing to internal member (and variations) didn't yield any useful results.

Edit: I know copy/move is broken for this. A request to the people who answer. If possible, please point me to the specification so that I can develop a habit of reading and interpreting it properly.

Upvotes: 1

Views: 90

Answers (2)

R Sahu
R Sahu

Reputation: 206577

Based on the comment

no, the members always point to other members, if at all.

I would suggest changing the member variables pint and pstr from being pointers to objects to pointers to member variables.

struct Foo {
    int m1{};
    int m2{};
    std::string str{};
    int Foo::* pint{nullptr};
    std::string Foo::* pstr{nullptr};
};

Then, you can use the default copy constructor and copy assignment operator without any problem. The following should work.

Foo a {10, 15, "Hello, Earth!", &Foo::m1, &Foo::str};

std::cout << a.*(a.pint) << std::endl;
std::cout << a.*(a.pstr) << std::endl;

// Default copy constructor works fine.
Foo b = a;
std::cout << b.*(b.pint) << std::endl;
std::cout << b.*(b.pstr) << std::endl;

// Default copy assignment works fine.
Foo c;
c = a;
std::cout << c.*(c.pint) << std::endl;
std::cout << c.*(c.pstr) << std::endl;

See it working at https://ideone.com/qE1WVn.


Re:

Could you also answer the last part of the question please (aggregate initialize the pointers inline to the members of the struct)?

You may also use

struct Foo {
    int m1{};
    int m2{};
    std::string str{};
    int Foo::* pint{&Foo::m1};
    std::string Foo::* pstr{&Foo::str};
};

and

Foo a {
    .m1 = 10,
    .m2 = 15,
    .str = "Hello, Earth!",
    .pint = &Foo::m2,
    .pstr = &Foo::str
};

Upvotes: 2

Yakk - Adam Nevraumont
Yakk - Adam Nevraumont

Reputation: 275385

This is legal, but the default copy/move operations are broken.

Either add

Foo(Foo const&)= delete;
Foo(Foo&&)=delete;
Foo&operator=(Foo const&)= delete;
Foo&operator=(Foo&&)=delete;

or implement them to do something sensible.

Upvotes: 3

Related Questions