Some time ago I proposed [1] new syntax for EBO. At that time the discussion devolved into an argument about the attribute syntax. I'm proposing it again, changing the syntax to avoid attributes.Library writers often find themselves wrapping possibly-empty objects in synthetic structs to take advantage of EBO:template <typename T, typename Allocator>class my_container {struct alloc_n_size : Allocator {size_t size;// ctor etc.} _M_alloc_n_size; // only occupies sizeof(size_t) if is_empty<Allocator>....};Would it not be more comfortable to supply some syntax for this:template <typename T, typename Allocator>class my_container {size_t _M_size;std::allow_zero_size<Allocator> _M_allocator;...};std::allow_zero_size<> is a library class template that uses compiler magic to tell the compiler that it is acceptable that the address of _M_allocator compare equal to that of some other object (that is, it need not insert padding if sizeof(Allocator) == 0). It overrides operator.() and friends so that _M_allocator can be used as if std::allow_zero_size<> was not specified.
From: Arthur O'Dwyer Sent: Monday, May 23, 2016 8:09 PM To: ISO C++ Standard - Future Proposals Reply To: std-pr...@isocpp.org Subject: [std-proposals] Re: Syntax for Empty Base Optimization (second attempt) |
To view this discussion on the web visit https://groups.google.com/a/isocpp.org/d/msgid/std-proposals/20160524001756.4890705.90511.11218%40gmail.com.
template<typename T>
stateless(is_empty_v<T>) struct allow_zero_size : public T
{
using T::T; //Forward constructors; inherit everything else.
};
std::allow_zero_size<> is a library class template that uses compiler magic to tell the compiler that it is acceptable that the address of _M_allocator compare equal to that of some other object (that is, it need not insert padding if sizeof(Allocator) == 0).
On Sunday, May 22, 2016 at 7:49:54 AM UTC-7, Avi Kivity wrote:Some time ago I proposed [1] new syntax for EBO. At that time the discussion devolved into an argument about the attribute syntax. I'm proposing it again, changing the syntax to avoid attributes.Library writers often find themselves wrapping possibly-empty objects in synthetic structs to take advantage of EBO:template <typename T, typename Allocator>class my_container {struct alloc_n_size : Allocator {size_t size;// ctor etc.} _M_alloc_n_size; // only occupies sizeof(size_t) if is_empty<Allocator>....};Would it not be more comfortable to supply some syntax for this:template <typename T, typename Allocator>class my_container {size_t _M_size;std::allow_zero_size<Allocator> _M_allocator;...};std::allow_zero_size<> is a library class template that uses compiler magic to tell the compiler that it is acceptable that the address of _M_allocator compare equal to that of some other object (that is, it need not insert padding if sizeof(Allocator) == 0). It overrides operator.() and friends so that _M_allocator can be used as if std::allow_zero_size<> was not specified.I think that if it were possible to implement such a std::allow_zero_size<T> today, everybody would be doing it (and that includes Boost).The hard part isn't so much the semantics of the allow_zero_size class template — that sounds great to me — but rather the problem is that there is no possible implementation of it today.
It's kind of like saying "wouldn't it be a good idea if an object named std::cout existed and we could just write std::cout << foo to print the value of any type at all", in the days before operator overloading existed. Those high-level semantics (arguably) sound great... but in order to implement those semantics, we need someone to do the core-language work of figuring out what it means to overload an operator, or in this case, to have an object with the same address as a different object.If I'm wrong and there does currently exist a (non-portable but) working proof-of-concept implementation of foo::allow_zero_size<T>, then that's awesome and I want to see it. And your proposal should include a link to it.
Some time ago I proposed [1] new syntax for EBO. At that time the discussion devolved into an argument about the attribute syntax. I'm proposing it again, changing the syntax to avoid attributes.
Note that EBO is actively dangerous. If you inherit from a class that defines a virtual member function that matches the signature of one of your own methods, then you end up overriding it for your EBO'd type.
void funcname() nonvirtual;
class foo : public final bar {...};
On Friday, June 10, 2016 at 12:16:23 PM UTC-4, Avi Kivity wrote:On Sunday, June 5, 2016 at 11:56:53 AM UTC+3, Marc wrote:On Sunday, May 22, 2016 at 4:49:54 PM UTC+2, Avi Kivity wrote:Some time ago I proposed [1] new syntax for EBO. At that time the discussion devolved into an argument about the attribute syntax. I'm proposing it again, changing the syntax to avoid attributes.
I believe that the best way of moving forward with this is to implement your proposal (the attribute version) as an extension in gcc ( https://gcc.gnu.org/bugzilla/show_bug.cgi?id=63579 ) or clang. That would include: write and test a patch, submit the patch to gcc/clang's mailing list, send a heads up to the cxx-abi-dev mailing list to give developers for other compilers a chance to comment on your exact ABI choices, start adding uses of this attribute in your code, boost, etc. And then you would be able to come to the committee with a stronger position.That is a very expensive way of moving forward. It requires me to learn the details of gcc/clang (both large projects with a high barrier to entry).I understand it for a complex proposal where there is a lot of effort needed anyway, but for small/trivial proposals like mine it's a good way to kill the proposal in its infancy.
Your proposal is most assuredly not trivial.
Your proposal requires changing how the compiler lays out a class; you declare an NSDM, but it somehow takes up no room.
Your proposal requires that the address of an object (the empty NSDM) need not be distinct from other unrelated objects.
And so forth.
I know it sounds rather burdensome to have to go through so much effort just to get something standardized. But despite how simple the idea sounds, you are still talking about a rather significant change to some very low-level parts of the system.
Empty base optimization is something that is much easier to do mechanically, because the conversion from derived class pointer to base class pointer is designed to not require the pointer value to change.
On sexta-feira, 10 de junho de 2016 09:13:09 PDT Avi Kivity wrote:
> Note that EBO is actively dangerous. If you inherit from a class that
> defines a virtual member function that matches the signature of one of your
> own methods, then you end up overriding it for your EBO'd type.
A class with virtuals is not empty.
An issue that could easily be fixed by adding one of two features:
1: The ability to declare a function which will *not* override from a base class virtual. That is, an explicit `nonvirtual`.
void funcname() nonvirtual;
2: The ability to declare that when inheriting from a class, you want to override nothing from that class. I would call this "final inheritance"; neither you nor your child classes can override virtual members of the specified base class.
class foo : public final bar {...};
Both of these would be a *lot* easier to implement than stateless members.
On Friday, June 10, 2016 at 8:30:20 PM UTC+3, Thiago Macieira wrote:On sexta-feira, 10 de junho de 2016 09:13:09 PDT Avi Kivity wrote:
> Note that EBO is actively dangerous. If you inherit from a class that
> defines a virtual member function that matches the signature of one of your
> own methods, then you end up overriding it for your EBO'd type.
A class with virtuals is not empty.You don't know that beforehand.template <class PossiblyEmptyComparator>struct my_container;Should my_container inherit from PossiblyEmptyComparator, or should it contain it as a data member?What if PossiblyEmptyComparator is a function pointer type?
On Fri, Jun 10, 2016 at 11:13 AM, Avi Kivity <a...@scylladb.com> wrote:On Friday, June 10, 2016 at 8:30:20 PM UTC+3, Thiago Macieira wrote:On sexta-feira, 10 de junho de 2016 09:13:09 PDT Avi Kivity wrote:
> Note that EBO is actively dangerous. If you inherit from a class that
> defines a virtual member function that matches the signature of one of your
> own methods, then you end up overriding it for your EBO'd type.
A class with virtuals is not empty.You don't know that beforehand.template <class PossiblyEmptyComparator>struct my_container;Should my_container inherit from PossiblyEmptyComparator, or should it contain it as a data member?What if PossiblyEmptyComparator is a function pointer type?Now you're no longer talking about EBO, though. You're talking about NEBP: the Non-Empty Base Pessimization. Obviously you should never name anything as a base class of yours if you don't know what's in it. Library implementors don't do that; why should you?
Instead, you'd do something roughly liketemplate<class PossiblyEmptyComparator, class Enable=void>struct ComparatorPlusInt {PossiblyEmptyComparator c;int i;PossiblyEmptyComparator& get_comparator() { return c; }int& get_int() { return i; }};template<class EmptyComparator>struct ComparatorPlusInt<EmptyComparator, enable_if_t<is_empty_v<EmptyComparator>>> : EmptyComparator {int i;EmptyComparator& get_comparator() { return *(EmptyComparator*)(this); }int& get_int() { return i; }};and then make a member of type ComparatorPlusInt<MyComparator>, which does the right thing either way. Your class doesn't need to trust the MyComparator class, because it's only ever inherited-from when it is empty (and even then, it's not inherited by your class but by the ComparatorPlusInt class).
Which I guess raises the question of whether std::pair<EmptyComparator, int> and/or std::pair<int, EmptyComparator> should be allowed and/or required to DTRT in this case. I don't actually know what the current wording is, and suspect that it might have the goal of allowing std::pair<X,Y> to be layout-equivalent to a POD struct whenever possible (which conflicts with the goal of making it as small as possible).I'm pretty sure you (Avi) know all this already, so I'm kind of confused how we got onto the topic of "what if my empty base class has virtual members", "what if my empty base class isn't a class at all",
etc. If you want better support for EBO, how about designing an EBO-friendly std::pair/std::tuple (which might already be done, for all I know); or else doing the heavy lifting of figuring out what it would mean for two non-zero-size objects to share a memory address; or else doing the heavy lifting of figuring out what it would mean for an object to have zero size.
Re your comments elsethread: C doesn't have a lot of these "heavy lifting" problems because it does allow zero-sized objects and it doesn't have a very strong type system the way C++ does. C++'s heavy lifting isn't in the machine-level implementation details; there you're right that the compilers can just "do what C does." The heavy lifting is in the C++-specific stuff: the high-level semantics, the language. That's the hard part.
--–Arthur
You received this message because you are subscribed to the Google Groups "ISO C++ Standard - Future Proposals" group.
To unsubscribe from this group and stop receiving emails from it, send an email to std-proposal...@isocpp.org.
To post to this group, send email to std-pr...@isocpp.org.
To view this discussion on the web visit https://groups.google.com/a/isocpp.org/d/msgid/std-proposals/CADvuK0KdC%3D%2BE%3D06_kbChDftPwtxB2V2t5ZgL7HPP7xQLLL0UCg%40mail.gmail.com.
Re your comments elsethread: C doesn't have a lot of these "heavy lifting" problems because it does allow zero-sized objects and it doesn't have a very strong type system the way C++ does. C++'s heavy lifting isn't in the machine-level implementation details; there you're right that the compilers can just "do what C does." The heavy lifting is in the C++-specific stuff: the high-level semantics, the language. That's the hard part.–Arthur
On Fri, Jun 10, 2016 at 10:07 PM, Arthur O'Dwyer <arthur....@gmail.com> wrote:On Fri, Jun 10, 2016 at 11:13 AM, Avi Kivity <a...@scylladb.com> wrote:On Friday, June 10, 2016 at 8:30:20 PM UTC+3, Thiago Macieira wrote:On sexta-feira, 10 de junho de 2016 09:13:09 PDT Avi Kivity wrote:
> Note that EBO is actively dangerous. If you inherit from a class that
> defines a virtual member function that matches the signature of one of your
> own methods, then you end up overriding it for your EBO'd type.
A class with virtuals is not empty.You don't know that beforehand.template <class PossiblyEmptyComparator>struct my_container;Should my_container inherit from PossiblyEmptyComparator, or should it contain it as a data member?What if PossiblyEmptyComparator is a function pointer type?Now you're no longer talking about EBO, though. You're talking about NEBP: the Non-Empty Base Pessimization. Obviously you should never name anything as a base class of yours if you don't know what's in it. Library implementors don't do that; why should you?They do it all the time, with exactly the example I gave. A colleague hit it recently with boost's binomial heap.Are you suggesting you should never inherit from a template parameter?
Because then template classes can choose from:1. Inheriting from the base class and hitting weird problems2. Using complex enable_if style solutions3. Eliding the optimization altogether.This could be so easily solved with4. Adding [[allow_empty_size]] attribute to the data member.
But I guess we must preserve C++'s reputation for making things hard on its users.
Re your comments elsethread: C doesn't have a lot of these "heavy lifting" problems because it does allow zero-sized objects and it doesn't have a very strong type system the way C++ does. C++'s heavy lifting isn't in the machine-level implementation details; there you're right that the compilers can just "do what C does." The heavy lifting is in the C++-specific stuff: the high-level semantics, the language. That's the hard part.
Then it's totally unreasonable for a compiler newbie like myself to try and figure them out. But can you explain where in the high level semantics a zero sized data member enters at all?
On Friday, June 10, 2016 at 8:02:25 PM UTC+3, Nicol Bolas wrote:On Friday, June 10, 2016 at 12:16:23 PM UTC-4, Avi Kivity wrote:On Sunday, June 5, 2016 at 11:56:53 AM UTC+3, Marc wrote:On Sunday, May 22, 2016 at 4:49:54 PM UTC+2, Avi Kivity wrote:Some time ago I proposed [1] new syntax for EBO. At that time the discussion devolved into an argument about the attribute syntax. I'm proposing it again, changing the syntax to avoid attributes.
I believe that the best way of moving forward with this is to implement your proposal (the attribute version) as an extension in gcc ( https://gcc.gnu.org/bugzilla/show_bug.cgi?id=63579 ) or clang. That would include: write and test a patch, submit the patch to gcc/clang's mailing list, send a heads up to the cxx-abi-dev mailing list to give developers for other compilers a chance to comment on your exact ABI choices, start adding uses of this attribute in your code, boost, etc. And then you would be able to come to the committee with a stronger position.That is a very expensive way of moving forward. It requires me to learn the details of gcc/clang (both large projects with a high barrier to entry).I understand it for a complex proposal where there is a lot of effort needed anyway, but for small/trivial proposals like mine it's a good way to kill the proposal in its infancy.
Your proposal is most assuredly not trivial.
Your proposal requires changing how the compiler lays out a class; you declare an NSDM, but it somehow takes up no room.C compilers (including gcc and clang) manage to do it just fine.
And so forth.Is there anything else?
Empty base optimization is something that is much easier to do mechanically, because the conversion from derived class pointer to base class pointer is designed to not require the pointer value to change.It is not easy to do mechanically, esp. for a template class. What if the class is final? What if the class is not an aggregate, but a primitive type? What if the class starts making member functions of the inheriting class virtual?
On Friday, June 10, 2016 at 3:21:50 PM UTC-4, Avi Kivity wrote:On Fri, Jun 10, 2016 at 10:07 PM, Arthur O'Dwyer <arthur....@gmail.com> wrote:On Fri, Jun 10, 2016 at 11:13 AM, Avi Kivity <a...@scylladb.com> wrote:On Friday, June 10, 2016 at 8:30:20 PM UTC+3, Thiago Macieira wrote:On sexta-feira, 10 de junho de 2016 09:13:09 PDT Avi Kivity wrote:
> Note that EBO is actively dangerous. If you inherit from a class that
> defines a virtual member function that matches the signature of one of your
> own methods, then you end up overriding it for your EBO'd type.
A class with virtuals is not empty.You don't know that beforehand.template <class PossiblyEmptyComparator>struct my_container;Should my_container inherit from PossiblyEmptyComparator, or should it contain it as a data member?What if PossiblyEmptyComparator is a function pointer type?Now you're no longer talking about EBO, though. You're talking about NEBP: the Non-Empty Base Pessimization. Obviously you should never name anything as a base class of yours if you don't know what's in it. Library implementors don't do that; why should you?They do it all the time, with exactly the example I gave. A colleague hit it recently with boost's binomial heap.Are you suggesting you should never inherit from a template parameter?
As he clearly said, you shouldn't inherit from something you don't know what it is.
Because then template classes can choose from:1. Inheriting from the base class and hitting weird problems2. Using complex enable_if style solutions3. Eliding the optimization altogether.This could be so easily solved with4. Adding [[allow_empty_size]] attribute to the data member.
Attributes are never allowed to change the behavior of a program. That's the rule with them. Period.
But I guess we must preserve C++'s reputation for making things hard on its users.
Re your comments elsethread: C doesn't have a lot of these "heavy lifting" problems because it does allow zero-sized objects and it doesn't have a very strong type system the way C++ does. C++'s heavy lifting isn't in the machine-level implementation details; there you're right that the compilers can just "do what C does." The heavy lifting is in the C++-specific stuff: the high-level semantics, the language. That's the hard part.Then it's totally unreasonable for a compiler newbie like myself to try and figure them out. But can you explain where in the high level semantics a zero sized data member enters at all?
... Everywhere? Is that a place?
C++ defines an object, first and foremost, as "a region of storage". The entire C++ object model relies on that. A zero-sized object is anathema to that definition.
You would have to rewrite a lot of the standard before you can permit zero-sized objects.
--
You received this message because you are subscribed to the Google Groups "ISO C++ Standard - Future Proposals" group.
To unsubscribe from this group and stop receiving emails from it, send an email to std-proposal...@isocpp.org.
To post to this group, send email to std-pr...@isocpp.org.
To view this discussion on the web visit https://groups.google.com/a/isocpp.org/d/msgid/std-proposals/53222639-9274-4dbc-a4fc-e57e94d9704c%40isocpp.org.
On sexta-feira, 10 de junho de 2016 11:13:41 PDT Avi Kivity wrote:
> On Friday, June 10, 2016 at 8:30:20 PM UTC+3, Thiago Macieira wrote:
> > On sexta-feira, 10 de junho de 2016 09:13:09 PDT Avi Kivity wrote:
> >
> > > Note that EBO is actively dangerous. If you inherit from a class that
> > > defines a virtual member function that matches the signature of one of
> >
> > your
> >
> > > own methods, then you end up overriding it for your EBO'd type.
> >
> > A class with virtuals is not empty.
>
> You don't know that beforehand.
Yes, you do, otherwise you're putting the cart ahead of the ox.
EBO is a solution for the problem of taking up space when you know the class
is empty. If you know it's not empty or you don't know, you don't derive from
that class.
--
Thiago Macieira - thiago (AT) macieira.info - thiago (AT) kde.org
Software Architect - Intel Open Source Technology Center
--
You received this message because you are subscribed to the Google Groups "ISO C++ Standard - Future Proposals" group.
To unsubscribe from this group and stop receiving emails from it, send an email to std-proposal...@isocpp.org.
To post to this group, send email to std-pr...@isocpp.org.
To view this discussion on the web visit https://groups.google.com/a/isocpp.org/d/msgid/std-proposals/2871170.DMf7rPM5rj%40tjmaciei-mobl1.
On Fri, Jun 10, 2016 at 10:38 PM, Nicol Bolas <jmck...@gmail.com> wrote:Re your comments elsethread: C doesn't have a lot of these "heavy lifting" problems because it does allow zero-sized objects and it doesn't have a very strong type system the way C++ does. C++'s heavy lifting isn't in the machine-level implementation details; there you're right that the compilers can just "do what C does." The heavy lifting is in the C++-specific stuff: the high-level semantics, the language. That's the hard part.Then it's totally unreasonable for a compiler newbie like myself to try and figure them out. But can you explain where in the high level semantics a zero sized data member enters at all?
... Everywhere? Is that a place?
C++ defines an object, first and foremost, as "a region of storage". The entire C++ object model relies on that. A zero-sized object is anathema to that definition.Would not the zero-sized object occupy a zero-sized region of storage?
You would have to rewrite a lot of the standard before you can permit zero-sized objects.Could you give me an example?
On Fri, Jun 10, 2016 at 10:53 PM, Thiago Macieira <thi...@macieira.org> wrote:On sexta-feira, 10 de junho de 2016 11:13:41 PDT Avi Kivity wrote:
> On Friday, June 10, 2016 at 8:30:20 PM UTC+3, Thiago Macieira wrote:
> > On sexta-feira, 10 de junho de 2016 09:13:09 PDT Avi Kivity wrote:
> >
> > > Note that EBO is actively dangerous. If you inherit from a class that
> > > defines a virtual member function that matches the signature of one of
> >
> > your
> >
> > > own methods, then you end up overriding it for your EBO'd type.
> >
> > A class with virtuals is not empty.
>
> You don't know that beforehand.
Yes, you do, otherwise you're putting the cart ahead of the ox.
EBO is a solution for the problem of taking up space when you know the class
is empty. If you know it's not empty or you don't know, you don't derive from
that class.Then it's very difficult to use EBO. You have to provide two specializations for the two cases, because in the general case, you know very little about the parameter.I'm trying to make EBO usable.
On Friday, June 10, 2016 at 3:07:19 PM UTC-4, Arthur O'Dwyer wrote:Re your comments elsethread: C doesn't have a lot of these "heavy lifting" problems because it does allow zero-sized objects and it doesn't have a very strong type system the way C++ does. C++'s heavy lifting isn't in the machine-level implementation details; there you're right that the compilers can just "do what C does." The heavy lifting is in the C++-specific stuff: the high-level semantics, the language. That's the hard part.
To be fair, the high level semantics are the easy part.
We know exactly what behavior we want from these things. [...]
On Fri, Jun 10, 2016 at 10:38 PM, Nicol Bolas <jmck...@gmail.com> wrote:On Friday, June 10, 2016 at 3:21:50 PM UTC-4, Avi Kivity wrote:On Fri, Jun 10, 2016 at 10:07 PM, Arthur O'Dwyer <arthur....@gmail.com> wrote:Now you're no longer talking about EBO, though. You're talking about NEBP: the Non-Empty Base Pessimization. Obviously you should never name anything as a base class of yours if you don't know what's in it. Library implementors don't do that; why should you?They do it all the time, with exactly the example I gave. A colleague hit it recently with boost's binomial heap.
Are you suggesting you should never inherit from a template parameter?
As he clearly said, you shouldn't inherit from something you don't know what it is.Well, then how can you apply EBO in a library? Say, std::unordered_set<Key, Hash, ...>, where Hash may and often is empty.
On Friday, June 10, 2016 at 3:48:02 PM UTC-4, Avi Kivity wrote:On Fri, Jun 10, 2016 at 10:38 PM, Nicol Bolas <jmck...@gmail.com> wrote:Re your comments elsethread: C doesn't have a lot of these "heavy lifting" problems because it does allow zero-sized objects and it doesn't have a very strong type system the way C++ does. C++'s heavy lifting isn't in the machine-level implementation details; there you're right that the compilers can just "do what C does." The heavy lifting is in the C++-specific stuff: the high-level semantics, the language. That's the hard part.Then it's totally unreasonable for a compiler newbie like myself to try and figure them out. But can you explain where in the high level semantics a zero sized data member enters at all?
... Everywhere? Is that a place?
C++ defines an object, first and foremost, as "a region of storage". The entire C++ object model relies on that. A zero-sized object is anathema to that definition.Would not the zero-sized object occupy a zero-sized region of storage?
And what exactly is "a zero-sized region of storage"? You cannot allocate or deallocate nothing. Even `malloc` doesn't work reasonably with zero. You may or may not get back a NULL pointer, but whatever you get back, you aren't allowed to dereference it.
What does it mean to have the address of, or a reference to, nothing?
What does it mean to perform pointer arithmetic on a pointer to nothing?
Can you have an array of nothing?
You would have to rewrite a lot of the standard before you can permit zero-sized objects.Could you give me an example?
You're the one proposing it. You're the one claiming that it's easy. The burden of proof here is on you.
--
You received this message because you are subscribed to the Google Groups "ISO C++ Standard - Future Proposals" group.
To unsubscribe from this group and stop receiving emails from it, send an email to std-proposal...@isocpp.org.
To post to this group, send email to std-pr...@isocpp.org.
To view this discussion on the web visit https://groups.google.com/a/isocpp.org/d/msgid/std-proposals/f4f103ad-587a-4f87-b48d-31c08b533366%40isocpp.org.
On sexta-feira, 10 de junho de 2016 23:11:21 PDT Avi Kivity wrote:
> > Yes, you do, otherwise you're putting the cart ahead of the ox.
> >
> > EBO is a solution for the problem of taking up space when you know the
> > class
> > is empty. If you know it's not empty or you don't know, you don't derive
> > from
> > that class.
>
> Then it's very difficult to use EBO. You have to provide two
> specializations for the two cases, because in the general case, you know
> very little about the parameter.
>
> I'm trying to make EBO usable.
You're again putting the cart ahead of the oxen.
You don't use EBO. Compilers aren't required to have that optimisation.
It just happens that some do and therefore library writers have used that
optimisation to to save space. The objective is to save space, not to derive.
--
Thiago Macieira - thiago (AT) macieira.info - thiago (AT) kde.org
Software Architect - Intel Open Source Technology Center
--
You received this message because you are subscribed to the Google Groups "ISO C++ Standard - Future Proposals" group.
To unsubscribe from this group and stop receiving emails from it, send an email to std-proposal...@isocpp.org.
To post to this group, send email to std-pr...@isocpp.org.
To view this discussion on the web visit https://groups.google.com/a/isocpp.org/d/msgid/std-proposals/3430372.t4tFe8WtuC%40tjmaciei-mobl1.
On Fri, Jun 10, 2016 at 12:47 PM, Avi Kivity <a...@scylladb.com> wrote:On Fri, Jun 10, 2016 at 10:38 PM, Nicol Bolas <jmck...@gmail.com> wrote:On Friday, June 10, 2016 at 3:21:50 PM UTC-4, Avi Kivity wrote:On Fri, Jun 10, 2016 at 10:07 PM, Arthur O'Dwyer <arthur....@gmail.com> wrote:Now you're no longer talking about EBO, though. You're talking about NEBP: the Non-Empty Base Pessimization. Obviously you should never name anything as a base class of yours if you don't know what's in it. Library implementors don't do that; why should you?They do it all the time, with exactly the example I gave. A colleague hit it recently with boost's binomial heap.Huh. I checked with the Boost code and then with Wandbox and you're correct, boost::heap::binomial_heap is incorrectly implemented. (It's also apparently been unmaintained for a while, as it produces a whole spew of warnings when compiled with Clang.) binomial_heap exposes (makes user-visible) private member functions such as allocate() and construct() which aren't supposed to exist in its interface; basically Boost is claiming that a heap is-an allocator, which is nonsense as far as I'm concerned.This may be defensible on the grounds of "everybody used to do it this way," or it may be indefensible, I'm not old enough to judge. :) But I do think that these days it's not the right way to do it.
Are you suggesting you should never inherit from a template parameter?
As he clearly said, you shouldn't inherit from something you don't know what it is.Well, then how can you apply EBO in a library? Say, std::unordered_set<Key, Hash, ...>, where Hash may and often is empty.The way I said (and provided code for). You pick one of your members to combine in a tuple with that possibly-empty member. Thanks to Howard Hinnant's reply in this thread, I gather that on good implementations (which I'm just going to blithely assume means "all popular implementations" ;)) you can do it viatemplate<class Key, class Hash, class EqualityComparator>class unordered_set {using bucket = std::list<Key>;std::tuple<Hash, EqualityComparator, std::vector<bucket>> m;auto&& hash() { return std::get<0>(m); }auto&& cmp() { return std::get<1>(m); }auto&& buckets() { return std::get<2>(m); }};unordered_set is-not-a hash, and is-not-a comparator; but it does have-a hash and have-a comparator. We write our code to express that relationship, and then we get correct and efficient code basically for free.
To the extent that std::tuple doesn't give us efficient code (e.g. if it orders the members wrong and thus wastes a lot of space on padding), vendors can go fix that; that's easy.
Or if users are demanding a way to implement tuple-like types (not identical to std::tuple) without so much metaprogramming, then that sounds like it might produce some kind of change. But it sounds basically like you're asking for zero-sized objects in C++ (which is unlikely to happen), and your particular use-case is already solved by std::tuple (modulo possible Quality of Implementation issues with some vendors), so there's not a lot of motivation to do anything about it.
–Arthur
--
You received this message because you are subscribed to the Google Groups "ISO C++ Standard - Future Proposals" group.
To unsubscribe from this group and stop receiving emails from it, send an email to std-proposal...@isocpp.org.
To post to this group, send email to std-pr...@isocpp.org.
To view this discussion on the web visit https://groups.google.com/a/isocpp.org/d/msgid/std-proposals/CADvuK0%2BhUnFaCsZva5pNRZFESfh44uTGu%3DPJ7xoikgpnRgNZwA%40mail.gmail.com.
On Sat, Jun 11, 2016 at 12:56 AM, Nicol Bolas <jmck...@gmail.com> wrote:On Friday, June 10, 2016 at 3:48:02 PM UTC-4, Avi Kivity wrote:On Fri, Jun 10, 2016 at 10:38 PM, Nicol Bolas <jmck...@gmail.com> wrote:Re your comments elsethread: C doesn't have a lot of these "heavy lifting" problems because it does allow zero-sized objects and it doesn't have a very strong type system the way C++ does. C++'s heavy lifting isn't in the machine-level implementation details; there you're right that the compilers can just "do what C does." The heavy lifting is in the C++-specific stuff: the high-level semantics, the language. That's the hard part.Then it's totally unreasonable for a compiler newbie like myself to try and figure them out. But can you explain where in the high level semantics a zero sized data member enters at all?
... Everywhere? Is that a place?
C++ defines an object, first and foremost, as "a region of storage". The entire C++ object model relies on that. A zero-sized object is anathema to that definition.Would not the zero-sized object occupy a zero-sized region of storage?
And what exactly is "a zero-sized region of storage"? You cannot allocate or deallocate nothing. Even `malloc` doesn't work reasonably with zero. You may or may not get back a NULL pointer, but whatever you get back, you aren't allowed to dereference it.That never comes into question. The zero-sized region is always part of a larger non-zero-sized region.Zero-sized regions seem to exist just fine in C++, when inheriting from an empty base class,
and in C, with empty struct members. Both C++ and C seem to have solved this problem.
What does it mean to have the address of, or a reference to, nothing?The same thing asstruct B {};struct D : B { int x; };D d;B* ptr_to_nothing = &d; // actually points at d.xB& ref_to_nothing = d;
B q;
B r;
memcpy(&q, &r, sizeof(B));
struct D { B b; };
D q;
D r;
memcpy(&q.b, r.b, sizeof(B));
struct D : B {...};
D q;
D r;
memcpy((B*)&q, (B*)&r, sizeof(B));
What does it mean to perform pointer arithmetic on a pointer to nothing?B* arithmetic = ptr_to_nothing + 1;
On Sat, Jun 11, 2016 at 12:56 AM, Nicol Bolas <jmck...@gmail.com> wrote:What does it mean to have the address of, or a reference to, nothing?The same thing asstruct B {};struct D : B { int x; };D d;B* ptr_to_nothing = &d; // actually points at d.xB& ref_to_nothing = d;
What does it mean to perform pointer arithmetic on a pointer to nothing?B* arithmetic = ptr_to_nothing + 1;
On sábado, 11 de junho de 2016 13:15:06 PDT Avi Kivity wrote:
> The same thing as
>
> struct B {};
> struct D : B { int x; };
> D d;
> B* ptr_to_nothing = &d; // actually points at d.x
> B& ref_to_nothing = d;
>
> > What does it mean to perform pointer arithmetic on a pointer to nothing?
>
> B* arithmetic = ptr_to_nothing + 1;
And where does this point to?
Also, what's "one past the last element" for zero-sized elements? The same
pointer, or different?
--
Thiago Macieira - thiago (AT) macieira.info - thiago (AT) kde.org
Software Architect - Intel Open Source Technology Center
--
You received this message because you are subscribed to the Google Groups "ISO C++ Standard - Future Proposals" group.
To unsubscribe from this group and stop receiving emails from it, send an email to std-proposal...@isocpp.org.
To post to this group, send email to std-pr...@isocpp.org.
To view this discussion on the web visit https://groups.google.com/a/isocpp.org/d/msgid/std-proposals/5862018.UHHoMLfmqT%40tjmaciei-mobl1.
On Saturday, June 11, 2016 at 6:15:28 AM UTC-4, Avi Kivity wrote:On Sat, Jun 11, 2016 at 12:56 AM, Nicol Bolas <jmck...@gmail.com> wrote:On Friday, June 10, 2016 at 3:48:02 PM UTC-4, Avi Kivity wrote:On Fri, Jun 10, 2016 at 10:38 PM, Nicol Bolas <jmck...@gmail.com> wrote:Re your comments elsethread: C doesn't have a lot of these "heavy lifting" problems because it does allow zero-sized objects and it doesn't have a very strong type system the way C++ does. C++'s heavy lifting isn't in the machine-level implementation details; there you're right that the compilers can just "do what C does." The heavy lifting is in the C++-specific stuff: the high-level semantics, the language. That's the hard part.Then it's totally unreasonable for a compiler newbie like myself to try and figure them out. But can you explain where in the high level semantics a zero sized data member enters at all?
... Everywhere? Is that a place?
C++ defines an object, first and foremost, as "a region of storage". The entire C++ object model relies on that. A zero-sized object is anathema to that definition.Would not the zero-sized object occupy a zero-sized region of storage?
And what exactly is "a zero-sized region of storage"? You cannot allocate or deallocate nothing. Even `malloc` doesn't work reasonably with zero. You may or may not get back a NULL pointer, but whatever you get back, you aren't allowed to dereference it.That never comes into question. The zero-sized region is always part of a larger non-zero-sized region.Zero-sized regions seem to exist just fine in C++, when inheriting from an empty base class,
But they're not zero-sized regions of memory. sizeof for the empty object will return non-zero.
and in C, with empty struct members. Both C++ and C seem to have solved this problem.
C++ only "solved that problem" by making specific exceptions to certain operations when dealing with base class subobjects. Do you know where you would have to make similar exceptions for member subobjects? Do you know if you can just piggy back off of that language, or would you have to scour the spec for locations where a member subobject that doesn't take up space would be problematic?
What does it mean to have the address of, or a reference to, nothing?The same thing asstruct B {};struct D : B { int x; };D d;B* ptr_to_nothing = &d; // actually points at d.xB& ref_to_nothing = d;
But `B` is not zero-sized. Nor is `D::B` zero sized.
The use of `B` as a base class simply does not take up room in the layout of `D`. That's a far cry from saying that `B` is zero-sized.
The other thing you're not getting is this.
This is perfectly legal:
B q;
B r;
memcpy(&q, &r, sizeof(B));
Because `B` is non-zero sized, that makes sense. `B` is trivially copyable, so you can copy it via memcpy.
This too is legal.
struct D { B b; };
D q;
D r;
memcpy(&q.b, r.b, sizeof(B));
This however, is not legal:
c
struct D : B {...};
D q;
D r;
memcpy((B*)&q, (B*)&r, sizeof(B));
While both B and D are trivially copyable, you are not allowed to trivially copy into a base-class subobject of another object. The standard explicitly forbids this in [basic.types]/2. Why?
Because it would break empty base optimization (among other things).
By standard layout rules, the presence of `B` as a base class of `D` does not distrub `D`'s layout. Therefore, a pointer to the `B` subobject must point to some storage within `D`. And that storage is probably taken up by one of the members of `D`. And since `B` is not zero-sized, copying anything into a base-class subobject can cause problems.
What does it mean to perform pointer arithmetic on a pointer to nothing?B* arithmetic = ptr_to_nothing + 1;
That's not an answer. What's the relationship between these two pointers? Are they pointing to the same object?
--
You received this message because you are subscribed to the Google Groups "ISO C++ Standard - Future Proposals" group.
To unsubscribe from this group and stop receiving emails from it, send an email to std-proposal...@isocpp.org.
To post to this group, send email to std-pr...@isocpp.org.
To view this discussion on the web visit https://groups.google.com/a/isocpp.org/d/msgid/std-proposals/838a4d44-20c8-4a6c-a276-76a5c8cb0f8d%40isocpp.org.
On Sat, Jun 11, 2016 at 6:51 PM, Nicol Bolas <jmck...@gmail.com> wrote:On Saturday, June 11, 2016 at 6:15:28 AM UTC-4, Avi Kivity wrote:On Sat, Jun 11, 2016 at 12:56 AM, Nicol Bolas <jmck...@gmail.com> wrote:On Friday, June 10, 2016 at 3:48:02 PM UTC-4, Avi Kivity wrote:On Fri, Jun 10, 2016 at 10:38 PM, Nicol Bolas <jmck...@gmail.com> wrote:Re your comments elsethread: C doesn't have a lot of these "heavy lifting" problems because it does allow zero-sized objects and it doesn't have a very strong type system the way C++ does. C++'s heavy lifting isn't in the machine-level implementation details; there you're right that the compilers can just "do what C does." The heavy lifting is in the C++-specific stuff: the high-level semantics, the language. That's the hard part.Then it's totally unreasonable for a compiler newbie like myself to try and figure them out. But can you explain where in the high level semantics a zero sized data member enters at all?
... Everywhere? Is that a place?
C++ defines an object, first and foremost, as "a region of storage". The entire C++ object model relies on that. A zero-sized object is anathema to that definition.Would not the zero-sized object occupy a zero-sized region of storage?
And what exactly is "a zero-sized region of storage"? You cannot allocate or deallocate nothing. Even `malloc` doesn't work reasonably with zero. You may or may not get back a NULL pointer, but whatever you get back, you aren't allowed to dereference it.That never comes into question. The zero-sized region is always part of a larger non-zero-sized region.Zero-sized regions seem to exist just fine in C++, when inheriting from an empty base class,
But they're not zero-sized regions of memory. sizeof for the empty object will return non-zero.Let me go back and explain what I want in detail so there are no misunderstandings.
...
Now to answer your question, both sizeof(A) and sizeof(c.a) will be 1, despite both objects taking up no space in either B or C. So the region of memory occupied by them is zero, despite their sizeof being non-zero. This is existing practice and is not introduced by my proposal.
and in C, with empty struct members. Both C++ and C seem to have solved this problem.
C++ only "solved that problem" by making specific exceptions to certain operations when dealing with base class subobjects. Do you know where you would have to make similar exceptions for member subobjects? Do you know if you can just piggy back off of that language, or would you have to scour the spec for locations where a member subobject that doesn't take up space would be problematic?I did not exhaustively read the standard looking for those places. But given that:1. the problem was solved for base classes2. the problem was solved, in C, for member objects3. the problem was solved, in at list gcc, for member objects, by declaring them as arrays of zero size.
it seems to be reasonable that there are no insurmountable difficulties. There's simply too much prior art to assume it is impossible or even difficult.
The other thing you're not getting is this.
This is perfectly legal:
B q;
B r;
memcpy(&q, &r, sizeof(B));
Because `B` is non-zero sized, that makes sense. `B` is trivially copyable, so you can copy it via memcpy.
This too is legal.
struct D { B b; };
D q;
D r;
memcpy(&q.b, r.b, sizeof(B));
This however, is not legal:
c
struct D : B {...};
D q;
D r;
memcpy((B*)&q, (B*)&r, sizeof(B));
While both B and D are trivially copyable, you are not allowed to trivially copy into a base-class subobject of another object. The standard explicitly forbids this in [basic.types]/2. Why?
Because it would break empty base optimization (among other things).
By standard layout rules, the presence of `B` as a base class of `D` does not distrub `D`'s layout. Therefore, a pointer to the `B` subobject must point to some storage within `D`. And that storage is probably taken up by one of the members of `D`. And since `B` is not zero-sized, copying anything into a base-class subobject can cause problems.Good. We can apply the same restriction to members annotated to take up no room in their struct's layout.
On sábado, 11 de junho de 2016 20:44:58 PDT Avi Kivity wrote:
> Let me go back and explain what I want in detail so there are no
> misunderstandings.
We understand what you want. But you fail to understand how difficult it is to
get what you want in the standard.
On Saturday, June 11, 2016 at 1:45:20 PM UTC-4, Avi Kivity wrote:On Sat, Jun 11, 2016 at 6:51 PM, Nicol Bolas <jmck...@gmail.com> wrote:On Saturday, June 11, 2016 at 6:15:28 AM UTC-4, Avi Kivity wrote:On Sat, Jun 11, 2016 at 12:56 AM, Nicol Bolas <jmck...@gmail.com> wrote:On Friday, June 10, 2016 at 3:48:02 PM UTC-4, Avi Kivity wrote:On Fri, Jun 10, 2016 at 10:38 PM, Nicol Bolas <jmck...@gmail.com> wrote:Re your comments elsethread: C doesn't have a lot of these "heavy lifting" problems because it does allow zero-sized objects and it doesn't have a very strong type system the way C++ does. C++'s heavy lifting isn't in the machine-level implementation details; there you're right that the compilers can just "do what C does." The heavy lifting is in the C++-specific stuff: the high-level semantics, the language. That's the hard part.Then it's totally unreasonable for a compiler newbie like myself to try and figure them out. But can you explain where in the high level semantics a zero sized data member enters at all?
... Everywhere? Is that a place?
C++ defines an object, first and foremost, as "a region of storage". The entire C++ object model relies on that. A zero-sized object is anathema to that definition.Would not the zero-sized object occupy a zero-sized region of storage?
And what exactly is "a zero-sized region of storage"? You cannot allocate or deallocate nothing. Even `malloc` doesn't work reasonably with zero. You may or may not get back a NULL pointer, but whatever you get back, you aren't allowed to dereference it.That never comes into question. The zero-sized region is always part of a larger non-zero-sized region.Zero-sized regions seem to exist just fine in C++, when inheriting from an empty base class,
But they're not zero-sized regions of memory. sizeof for the empty object will return non-zero.Let me go back and explain what I want in detail so there are no misunderstandings....Now to answer your question, both sizeof(A) and sizeof(c.a) will be 1, despite both objects taking up no space in either B or C. So the region of memory occupied by them is zero, despite their sizeof being non-zero. This is existing practice and is not introduced by my proposal.
If that's your idea, then why do you keep bringing up zero-sized arrays and zero-sized types? That is, you keep saying that C allows for empty members, but it does so with a completely different mechanism: by allowing types to have zero size.
So don't bring up empty types in C unless that's actually what you want.
and in C, with empty struct members. Both C++ and C seem to have solved this problem.
C++ only "solved that problem" by making specific exceptions to certain operations when dealing with base class subobjects. Do you know where you would have to make similar exceptions for member subobjects? Do you know if you can just piggy back off of that language, or would you have to scour the spec for locations where a member subobject that doesn't take up space would be problematic?I did not exhaustively read the standard looking for those places. But given that:1. the problem was solved for base classes2. the problem was solved, in C, for member objects3. the problem was solved, in at list gcc, for member objects, by declaring them as arrays of zero size.it seems to be reasonable that there are no insurmountable difficulties. There's simply too much prior art to assume it is impossible or even difficult.
As previously stated, #2 and #3 are non-sequiturs for this conversation.
That only leaves #1. And you also clearly state that you haven't looked for such places. And therefore, you don't know if the solutions for base classes will work for members. You also don't know if you would need to make other adjustments that the base class solution didn't need.
So the basis for your claim is flimsy.
The other thing you're not getting is this.
This is perfectly legal:
B q;
B r;
memcpy(&q, &r, sizeof(B));
Because `B` is non-zero sized, that makes sense. `B` is trivially copyable, so you can copy it via memcpy.
This too is legal.
struct D { B b; };
D q;
D r;
memcpy(&q.b, r.b, sizeof(B));
This however, is not legal:
c
struct D : B {...};
D q;
D r;
memcpy((B*)&q, (B*)&r, sizeof(B));
While both B and D are trivially copyable, you are not allowed to trivially copy into a base-class subobject of another object. The standard explicitly forbids this in [basic.types]/2. Why?
Because it would break empty base optimization (among other things).
By standard layout rules, the presence of `B` as a base class of `D` does not distrub `D`'s layout. Therefore, a pointer to the `B` subobject must point to some storage within `D`. And that storage is probably taken up by one of the members of `D`. And since `B` is not zero-sized, copying anything into a base-class subobject can cause problems.Good. We can apply the same restriction to members annotated to take up no room in their struct's layout.
OK, the point of my spiel here was to explain to you just how little you understand the ramifications of what you ask. Until I brought that up, you had no idea that this copying thing was even an issue with your idea. Which proves that you simply do not know much about the standardization issues of permitting empty members.
And that's just one thing. How many others are there? You certainly don't know.
Yet you continue to claim that it will not be difficult. Why should we believe you about how difficult this is, when you have repeatedly displayed your ignorance on the complexities of such a feature?
Not to mention, when asked to demonstrate how "not difficult" it would be by actually implementing it, you balked and claimed that this request was somehow unfair.
Anyone can say "go do this; it should be easy." It's far easier to do that than to actually implement it or learn about the particulars of the spec so that you can get the feature's wording right.
--
You received this message because you are subscribed to the Google Groups "ISO C++ Standard - Future Proposals" group.
To unsubscribe from this group and stop receiving emails from it, send an email to std-proposal...@isocpp.org.
To post to this group, send email to std-pr...@isocpp.org.
To view this discussion on the web visit https://groups.google.com/a/isocpp.org/d/msgid/std-proposals/CAF950WLg7xjAV%2BN5%3DUddZfbjuQH8rkRo4RVXs_D4r-dBgRP0Ww%40mail.gmail.com.
On Sun, Jun 12, 2016 at 2:51 AM, Nicol Bolas <jmck...@gmail.com> wrote:On Saturday, June 11, 2016 at 1:45:20 PM UTC-4, Avi Kivity wrote:Now to answer your question, both sizeof(A) and sizeof(c.a) will be 1, despite both objects taking up no space in either B or C. So the region of memory occupied by them is zero, despite their sizeof being non-zero. This is existing practice and is not introduced by my proposal.
If that's your idea, then why do you keep bringing up zero-sized arrays and zero-sized types? That is, you keep saying that C allows for empty members, but it does so with a completely different mechanism: by allowing types to have zero size.No. I am proposing to use the same mechanism that C++ uses for empty bases (which are not zero sized), and that C uses for empty members (which in C also happen to be zero sized), and that gcc uses for array members of size zero (which are zero sized).
and in C, with empty struct members. Both C++ and C seem to have solved this problem.
C++ only "solved that problem" by making specific exceptions to certain operations when dealing with base class subobjects. Do you know where you would have to make similar exceptions for member subobjects? Do you know if you can just piggy back off of that language, or would you have to scour the spec for locations where a member subobject that doesn't take up space would be problematic?I did not exhaustively read the standard looking for those places. But given that:1. the problem was solved for base classes2. the problem was solved, in C, for member objects3. the problem was solved, in at list gcc, for member objects, by declaring them as arrays of zero size.it seems to be reasonable that there are no insurmountable difficulties. There's simply too much prior art to assume it is impossible or even difficult.
As previously stated, #2 and #3 are non-sequiturs for this conversation.They are not non sequiturs. They are similar to my case but not exact. If only things that were in the standard were allowed to be standardized, we'd never get anywhere.
That only leaves #1. And you also clearly state that you haven't looked for such places. And therefore, you don't know if the solutions for base classes will work for members. You also don't know if you would need to make other adjustments that the base class solution didn't need.I don't know, that is why I am asking the collective wisdom of this list. And the answers I'm getting are "it's everywhere" and "go look yourself" which are just symptoms of automatic rejection, not of anyone knowing any actual objection.
On Friday, June 10, 2016 at 3:21:50 PM UTC-4, Avi Kivity wrote:
On Fri, Jun 10, 2016 at 11:13 AM, Avi Kivity <a...@scylladb.com> wrote:
On Friday, June 10, 2016 at 8:30:20 PM UTC+3, Thiago Macieira wrote:On sexta-feira, 10 de junho de 2016 09:13:09 PDT Avi Kivity wrote:
> Note that EBO is actively dangerous. If you inherit from a class that
> defines a virtual member function that matches the signature of one of your
> own methods, then you end up overriding it for your EBO'd type.
A class with virtuals is not empty.You don't know that beforehand.
template <class PossiblyEmptyComparator>struct my_container;Should my_container inherit from PossiblyEmptyComparator, or should it contain it as a data member?What if PossiblyEmptyComparator is a function pointer type?
Now you're no longer talking about EBO, though. You're talking about NEBP: the Non-Empty Base Pessimization. Obviously you should never name anything as a base class of yours if you don't know what's in it. Library implementors don't do that; why should you?
They do it all the time, with exactly the example I gave. A colleague hit it recently with boost's binomial heap.
Are you suggesting you should never inherit from a template parameter?
As he clearly said, you shouldn't inherit from something you don't know what it is.
Because then template classes can choose from:
1. Inheriting from the base class and hitting weird problems2. Using complex enable_if style solutions3. Eliding the optimization altogether.This could be so easily solved with4. Adding [[allow_empty_size]] attribute to the data member.
Attributes are never allowed to change the behavior of a program. That's the rule with them. Period.
But I guess we must preserve C++'s reputation for making things hard on its users.
Re your comments elsethread: C doesn't have a lot of these "heavy lifting" problems because it does allow zero-sized objects and it doesn't have a very strong type system the way C++ does. C++'s heavy lifting isn't in the machine-level implementation details; there you're right that the compilers can just "do what C does." The heavy lifting is in the C++-specific stuff: the high-level semantics, the language. That's the hard part.
Then it's totally unreasonable for a compiler newbie like myself to try and figure them out. But can you explain where in the high level semantics a zero sized data member enters at all?
... Everywhere? Is that a place?
C++ defines an object, first and foremost, as "a region of storage". The entire C++ object model relies on that. A zero-sized object is anathema to that definition. You would have to rewrite a lot of the standard before you can permit zero-sized objects.
在 2016年6月11日星期六 UTC+8上午3:38:05,Nicol Bolas写道:
Attributes are never allowed to change the behavior of a program. That's the rule with them. Period.
Citation needed. I only remember Herb Sutter claims that attributes should never allowed to change the semantics.
Both of them are doubtful in the aspect of effect, since they are not requirements in the normative standard text. Even the published standards violate these rules. Also note [dcl.align] is [dcl.attr]. Do you think it is a defect?
On Tuesday, June 14, 2016 at 3:41:27 AM UTC-4, FrankHB1989 wrote:在 2016年6月11日星期六 UTC+8上午3:38:05,Nicol Bolas写道:Attributes are never allowed to change the behavior of a program. That's the rule with them. Period.Citation needed. I only remember Herb Sutter claims that attributes should never allowed to change the semantics.
Semantics, behavior; it's effectively the same thing: could you tell the difference in the program if the attribute weren't present?
If the answer is yes, then it can't be an attribute. And since attributes-as-behavior/semantics is clearly on Herb Sutter's "over my dead body" list, using attributes to change the program's visible behavior is a non-starter.
Both of them are doubtful in the aspect of effect, since they are not requirements in the normative standard text. Even the published standards violate these rules. Also note [dcl.align] is [dcl.attr]. Do you think it is a defect?
While `alignas` is grammatically an `attribute-specifier`, it is grammatically not an `attribute`. It isn't in an `attribute-list`, so it doesn't go within a [[]] pair. So it doesn't count.
在 2016年6月15日星期三 UTC+8上午1:19:56,Nicol Bolas写道:On Tuesday, June 14, 2016 at 3:41:27 AM UTC-4, FrankHB1989 wrote:
Both of them are doubtful in the aspect of effect, since they are not requirements in the normative standard text. Even the published standards violate these rules. Also note [dcl.align] is [dcl.attr]. Do you think it is a defect?
While `alignas` is grammatically an `attribute-specifier`, it is grammatically not an `attribute`. It isn't in an `attribute-list`, so it doesn't go within a [[]] pair. So it doesn't count.
While I agree with you on the point of grammar, the term "attribute" seems to be not only used as a syntactic category.