struct
empty_policy
{
/*static*/ void do_sth();
};
template <class Policy>
class
generic_type
{
public:
void
do_sth_fancy()
{
++value;
policy.do_sth();
}
private:
int value;
Policy policy;
};
const Policy&
get_policy() const;
With all those changes usage would look like so:
Enter code here...
struct
empty_policy
{
/*static*/ void
do_sth();
};
template <class Policy>
class
generic_type
{
public:
void
do_sth_fancy()
{
++value;
policy.do_sth();
}
std::empty_reference<const Policy>
get_policy() const
{
return policy;
}
private:
int
value;
[[maybe_empty]] Policy
policy;
};
This is just a rough sketch, there might be issues (like the copy/move ctors/assignment operators for empty types and their ability to affect the optimization),
but what do you think?
Jonathan MüllerThis is a suggestion for a different approach to "solve" EBO.
Responses to Allow zero size arrays and let them occupy zero bytes mentioned that the C++ object model cannot have anything that has size 0.
So I got a completely different idea based on the fact that empty types are only needed for their member functions.
- (optional, required for current empty types to work) Make member functions that don't access any non-static member variables or functions implicitly static.
struct Empty1 { void DoSomething() {}};
struct Empty2 {Empty 1 e; void DoSomething() {e.DoSomething();}};struct Empty
{
void DoSomething();
};
struct Full
{
int foo;
Empty bar;
};inline void Empty::DoSomething() {}
struct Full2
{
int foo;
Empty bar;
};
- This shouldn't break any existing code because you can call a static member function with the same syntax as a non-member function, so any code calling a member function can also call a static function without change.
- "Encourage" - like with the RVO - compilers to remove objects that are only used to call static member functions. Of course once the actual object is required (i.e. if you take the address of it), this optimization cannot happen.
With these changes you can simply write the following generic code:
struct empty_policy
{
/*static*/ void do_sth();
};
template <class Policy>
class generic_type
{
public:
void do_sth_fancy()
{
++value;
policy.do_sth();
}
private:
int value;
Policy policy;
};
Because policy is only used to call a (implictly) static member function, it will be optimized out and sizeof(generic_type<empty_policy>) will be sizeof(int).
If you instantiate it with a non-empty type, however, this is not the case.
This approach is cumbersome, however: It is very easy to write code that depends on the address of policy,
I thus also suggest:
- A new attribute
This is just a rough sketch, there might be issues (like the copy/move ctors/assignment operators for empty types and their ability to affect the optimization),
but what do you think?
Why do people keep talking about this in terms of EBO, like that's the only reason why you would ever want a member to take up no space?
So, a member function which calls a sibling member function that doesn't access NSDMs wouldn't be allowed? That's a pretty harsh requirement. And a very unnecessary one.
And what if the type has its own member which is empty by this definition? Are you saying that nested empty members don't work:struct Empty1 { void DoSomething() {}};
struct Empty2 {Empty 1 e; void DoSomething() {e.DoSomething();}};
Why should `Empty2` not be just as empty as `Empty1`?
Also, you are now doing something never before seen in C++: you are changing the definition of a class based on the definition of one of its member functions. Naturally that requires those member functions to be inline. Consider this:struct Empty
{
void DoSomething();
};
struct Full
{
int foo;
Empty bar;
};
You are saying that `sizeof(Full)` depends on the eventual definition of `DoSomething`. This means that compilers have to do one of the following:
You said "will be". If that's the case, then this is not like RBO, since that's not required. So is this required or is it not?
No. Stop using attributes to solve problems. Especially this one.
You're essentially trying to define away the problem by making the object not actually be a member. It's an interesting solution to the problem, but I don't think it's the best way to handle it.
On Thursday, July 7, 2016 at 4:22:46 PM UTC+2, Nicol Bolas wrote:Why do people keep talking about this in terms of EBO, like that's the only reason why you would ever want a member to take up no space?No, because that's the only way to have empty members.
So every solution would be a replacement for EBO.
So, a member function which calls a sibling member function that doesn't access NSDMs wouldn't be allowed? That's a pretty harsh requirement. And a very unnecessary one.Many negations, let me grasp this:
A member function that calls a sibling member function.
The sibling member function doesn't access NSDMS, thus it is implictly static.
And thus the original member function is implictly static.
And what if the type has its own member which is empty by this definition? Are you saying that nested empty members don't work:struct Empty1 { void DoSomething() {}};
struct Empty2 {Empty 1 e; void DoSomething() {e.DoSomething();}};
Why should `Empty2` not be just as empty as `Empty1`?`Empty2` should be just as empty as `Empty1`, yes.
Also, you are now doing something never before seen in C++: you are changing the definition of a class based on the definition of one of its member functions. Naturally that requires those member functions to be inline. Consider this:struct Empty
{
void DoSomething();
};
struct Full
{
int foo;
Empty bar;
};
You are saying that `sizeof(Full)` depends on the eventual definition of `DoSomething`. This means that compilers have to do one of the following:No, because `Empty` doesn't have any member variables, `DoSomething` cannot possibly access any member variables and is thus implicitly static.
static Empty *ptr = nullptr;
void Empty::DoSomething()
{
ptr = this;
}void Empty::DoSomething()
{
std::sort(..., [this](const auto &lhs, const auto &rhs) {...});
}You said "will be". If that's the case, then this is not like RBO, since that's not required. So is this required or is it not?My mistake, I meant copy elision, not RVO. It is required.
This is a suggestion for a different approach to "solve" EBO.
Responses to Allow zero size arrays and let them occupy zero bytes mentioned that the C++ object model cannot have anything that has size 0.
So I got a completely different idea based on the fact that empty types are only needed for their member functions.
- (optional, required for current empty types to work) Make member functions that don't access any non-static member variables or functions implicitly static.
This is a suggestion for a different approach to "solve" EBO.
Responses to Allow zero size arrays and let them occupy zero bytes mentioned that the C++ object model cannot have anything that has size 0.
So I got a completely different idea based on the fact that empty types are only needed for their member functions.
- (optional, required for current empty types to work) Make member functions that don't access any non-static member variables or functions implicitly static.
This shouldn't break any existing code because you can call a static member function with the same syntax as a non-member function, so any code calling a member function can also call a static function without change.
struct A {
int not_visible();
int not_sure_if_implicitly_static_or_not() { return not_visible(); }
};struct B {
int oops() { return 3; }
int oops() const { return 4; }
};struct B {
int implicitly_static() { return 3; }
};
template<class T>
auto sfinae(const T& t) -> decltype(t.implicitly_static())
{
return 3;
}
... sfinae(B()) ...