Standard-layout and tail padding











up vote
13
down vote

favorite
2












David Hollman recently tweeted the following example (which I've slightly reduced):



struct FooBeforeBase {
double d;
bool b[4];
};

struct FooBefore : FooBeforeBase {
float value;
};

static_assert(sizeof(FooBefore) > 16);

//----------------------------------------------------

struct FooAfterBase {
protected:
double d;
public:
bool b[4];
};

struct FooAfter : FooAfterBase {
float value;
};

static_assert(sizeof(FooAfter) == 16);


You can examine the layout in clang on godbolt and see that the reason the size changed is that in FooBefore, the member value is placed at offset 16 (maintaining a full alignment of 8 from FooBeforeBase) whereas in FooAfter, the member value is placed at offset 12 (effectively using FooAfterBase's tail-padding).



It is clear to me that FooBeforeBase is standard-layout, but FooAfterBase is not (because its non-static data members do not all have the same access control, [class.prop]/3). But what is it about FooBeforeBase's being standard-layout that requires this respect of padding bytes?



Both gcc and clang reuse FooAfterBase's padding, ending up with sizeof(FooAfter) == 16. But MSVC does not, ending up with 24. Is there a required layout per the standard and, if not, why do gcc and clang do what they do?





There is some confusion, so just to clear up:





  • FooBeforeBase is standard-layout


  • FooBefore is not (both it and a base class have non-static data members, similar to E in this example)


  • FooAfterBase is not (it has non-static data members of differing access)


  • FooAfter is not (for both of the above reasons)










share|improve this question
























  • Who says that this behavior is "required" by anything in the standard? It could simply be a manifestation of how the compiler goes about implementing things.
    – Nicol Bolas
    4 hours ago










  • @NicolBolas It may very well not be required. MSVC does not do this (its FooAfter is also 24 bytes), but gcc and clang do - and it seems like that's a conscious choice on their parts.
    – Barry
    4 hours ago










  • "it seems like that's a conscious choice on their parts." What makes you say that?
    – Nicol Bolas
    4 hours ago








  • 1




    Related: stackoverflow.com/a/51334730/775806
    – n.m.
    4 hours ago










  • It is never required that there is no padding between class members. It may be required that there is padding. So the correct question is not which part of the standard requires gcc to reuse the end-padding, but what allows it to do so in the second case. Another question is whether something disallows such reuse in the first case.
    – n.m.
    4 hours ago

















up vote
13
down vote

favorite
2












David Hollman recently tweeted the following example (which I've slightly reduced):



struct FooBeforeBase {
double d;
bool b[4];
};

struct FooBefore : FooBeforeBase {
float value;
};

static_assert(sizeof(FooBefore) > 16);

//----------------------------------------------------

struct FooAfterBase {
protected:
double d;
public:
bool b[4];
};

struct FooAfter : FooAfterBase {
float value;
};

static_assert(sizeof(FooAfter) == 16);


You can examine the layout in clang on godbolt and see that the reason the size changed is that in FooBefore, the member value is placed at offset 16 (maintaining a full alignment of 8 from FooBeforeBase) whereas in FooAfter, the member value is placed at offset 12 (effectively using FooAfterBase's tail-padding).



It is clear to me that FooBeforeBase is standard-layout, but FooAfterBase is not (because its non-static data members do not all have the same access control, [class.prop]/3). But what is it about FooBeforeBase's being standard-layout that requires this respect of padding bytes?



Both gcc and clang reuse FooAfterBase's padding, ending up with sizeof(FooAfter) == 16. But MSVC does not, ending up with 24. Is there a required layout per the standard and, if not, why do gcc and clang do what they do?





There is some confusion, so just to clear up:





  • FooBeforeBase is standard-layout


  • FooBefore is not (both it and a base class have non-static data members, similar to E in this example)


  • FooAfterBase is not (it has non-static data members of differing access)


  • FooAfter is not (for both of the above reasons)










share|improve this question
























  • Who says that this behavior is "required" by anything in the standard? It could simply be a manifestation of how the compiler goes about implementing things.
    – Nicol Bolas
    4 hours ago










  • @NicolBolas It may very well not be required. MSVC does not do this (its FooAfter is also 24 bytes), but gcc and clang do - and it seems like that's a conscious choice on their parts.
    – Barry
    4 hours ago










  • "it seems like that's a conscious choice on their parts." What makes you say that?
    – Nicol Bolas
    4 hours ago








  • 1




    Related: stackoverflow.com/a/51334730/775806
    – n.m.
    4 hours ago










  • It is never required that there is no padding between class members. It may be required that there is padding. So the correct question is not which part of the standard requires gcc to reuse the end-padding, but what allows it to do so in the second case. Another question is whether something disallows such reuse in the first case.
    – n.m.
    4 hours ago















up vote
13
down vote

favorite
2









up vote
13
down vote

favorite
2






2





David Hollman recently tweeted the following example (which I've slightly reduced):



struct FooBeforeBase {
double d;
bool b[4];
};

struct FooBefore : FooBeforeBase {
float value;
};

static_assert(sizeof(FooBefore) > 16);

//----------------------------------------------------

struct FooAfterBase {
protected:
double d;
public:
bool b[4];
};

struct FooAfter : FooAfterBase {
float value;
};

static_assert(sizeof(FooAfter) == 16);


You can examine the layout in clang on godbolt and see that the reason the size changed is that in FooBefore, the member value is placed at offset 16 (maintaining a full alignment of 8 from FooBeforeBase) whereas in FooAfter, the member value is placed at offset 12 (effectively using FooAfterBase's tail-padding).



It is clear to me that FooBeforeBase is standard-layout, but FooAfterBase is not (because its non-static data members do not all have the same access control, [class.prop]/3). But what is it about FooBeforeBase's being standard-layout that requires this respect of padding bytes?



Both gcc and clang reuse FooAfterBase's padding, ending up with sizeof(FooAfter) == 16. But MSVC does not, ending up with 24. Is there a required layout per the standard and, if not, why do gcc and clang do what they do?





There is some confusion, so just to clear up:





  • FooBeforeBase is standard-layout


  • FooBefore is not (both it and a base class have non-static data members, similar to E in this example)


  • FooAfterBase is not (it has non-static data members of differing access)


  • FooAfter is not (for both of the above reasons)










share|improve this question















David Hollman recently tweeted the following example (which I've slightly reduced):



struct FooBeforeBase {
double d;
bool b[4];
};

struct FooBefore : FooBeforeBase {
float value;
};

static_assert(sizeof(FooBefore) > 16);

//----------------------------------------------------

struct FooAfterBase {
protected:
double d;
public:
bool b[4];
};

struct FooAfter : FooAfterBase {
float value;
};

static_assert(sizeof(FooAfter) == 16);


You can examine the layout in clang on godbolt and see that the reason the size changed is that in FooBefore, the member value is placed at offset 16 (maintaining a full alignment of 8 from FooBeforeBase) whereas in FooAfter, the member value is placed at offset 12 (effectively using FooAfterBase's tail-padding).



It is clear to me that FooBeforeBase is standard-layout, but FooAfterBase is not (because its non-static data members do not all have the same access control, [class.prop]/3). But what is it about FooBeforeBase's being standard-layout that requires this respect of padding bytes?



Both gcc and clang reuse FooAfterBase's padding, ending up with sizeof(FooAfter) == 16. But MSVC does not, ending up with 24. Is there a required layout per the standard and, if not, why do gcc and clang do what they do?





There is some confusion, so just to clear up:





  • FooBeforeBase is standard-layout


  • FooBefore is not (both it and a base class have non-static data members, similar to E in this example)


  • FooAfterBase is not (it has non-static data members of differing access)


  • FooAfter is not (for both of the above reasons)







c++ g++ language-lawyer clang++ standard-layout






share|improve this question















share|improve this question













share|improve this question




share|improve this question








edited 4 hours ago

























asked 5 hours ago









Barry

176k18299557




176k18299557












  • Who says that this behavior is "required" by anything in the standard? It could simply be a manifestation of how the compiler goes about implementing things.
    – Nicol Bolas
    4 hours ago










  • @NicolBolas It may very well not be required. MSVC does not do this (its FooAfter is also 24 bytes), but gcc and clang do - and it seems like that's a conscious choice on their parts.
    – Barry
    4 hours ago










  • "it seems like that's a conscious choice on their parts." What makes you say that?
    – Nicol Bolas
    4 hours ago








  • 1




    Related: stackoverflow.com/a/51334730/775806
    – n.m.
    4 hours ago










  • It is never required that there is no padding between class members. It may be required that there is padding. So the correct question is not which part of the standard requires gcc to reuse the end-padding, but what allows it to do so in the second case. Another question is whether something disallows such reuse in the first case.
    – n.m.
    4 hours ago




















  • Who says that this behavior is "required" by anything in the standard? It could simply be a manifestation of how the compiler goes about implementing things.
    – Nicol Bolas
    4 hours ago










  • @NicolBolas It may very well not be required. MSVC does not do this (its FooAfter is also 24 bytes), but gcc and clang do - and it seems like that's a conscious choice on their parts.
    – Barry
    4 hours ago










  • "it seems like that's a conscious choice on their parts." What makes you say that?
    – Nicol Bolas
    4 hours ago








  • 1




    Related: stackoverflow.com/a/51334730/775806
    – n.m.
    4 hours ago










  • It is never required that there is no padding between class members. It may be required that there is padding. So the correct question is not which part of the standard requires gcc to reuse the end-padding, but what allows it to do so in the second case. Another question is whether something disallows such reuse in the first case.
    – n.m.
    4 hours ago


















Who says that this behavior is "required" by anything in the standard? It could simply be a manifestation of how the compiler goes about implementing things.
– Nicol Bolas
4 hours ago




Who says that this behavior is "required" by anything in the standard? It could simply be a manifestation of how the compiler goes about implementing things.
– Nicol Bolas
4 hours ago












@NicolBolas It may very well not be required. MSVC does not do this (its FooAfter is also 24 bytes), but gcc and clang do - and it seems like that's a conscious choice on their parts.
– Barry
4 hours ago




@NicolBolas It may very well not be required. MSVC does not do this (its FooAfter is also 24 bytes), but gcc and clang do - and it seems like that's a conscious choice on their parts.
– Barry
4 hours ago












"it seems like that's a conscious choice on their parts." What makes you say that?
– Nicol Bolas
4 hours ago






"it seems like that's a conscious choice on their parts." What makes you say that?
– Nicol Bolas
4 hours ago






1




1




Related: stackoverflow.com/a/51334730/775806
– n.m.
4 hours ago




Related: stackoverflow.com/a/51334730/775806
– n.m.
4 hours ago












It is never required that there is no padding between class members. It may be required that there is padding. So the correct question is not which part of the standard requires gcc to reuse the end-padding, but what allows it to do so in the second case. Another question is whether something disallows such reuse in the first case.
– n.m.
4 hours ago






It is never required that there is no padding between class members. It may be required that there is padding. So the correct question is not which part of the standard requires gcc to reuse the end-padding, but what allows it to do so in the second case. Another question is whether something disallows such reuse in the first case.
– n.m.
4 hours ago














5 Answers
5






active

oldest

votes

















up vote
3
down vote













The answer to this question doesn't come from the standard but rather from the Itanium ABI (which is why gcc and clang have one behavior but msvc does something else). That ABI defines a layout, the relevant parts of which for the purposes of this question are:




For purposes internal to the specification, we also specify:





  • dsize(O): the data size of an object, which is the size of O without tail padding.




and




We ignore tail padding for PODs because an early version of the standard did not allow us to use it for anything else and because it sometimes permits faster copying of the type.




Where the placement of members other than virtual base classes is defined as:




Start at offset dsize(C), incremented if necessary for alignment to nvalign(D) for base classes or to align(D) for data members. Place D at this offset unless [... not relevant ...].




The term POD has disappeared from the C++ standard, but it means standard-layout and trivially copyable. In this question, FooBeforeBase is a POD. The Itanium ABI ignores tail padding - hence dsize(FooBeforeBase) is 16.



But FooAfterBase is not a POD (it is trivially copyable, but it is not standard-layout). As a result, tail padding is not ignored, so dsize(FooAfterBase) is just 12, and the float can go right there.



This has interesting consequences, as pointed out by Quuxplusone in a related answer, implementors also typically assume that tail padding isn't reused, which wreaks havoc on this example:




#include <algorithm>
#include <stdio.h>

struct A {
int m_a;
};

struct B : A {
int m_b1;
char m_b2;
};

struct C : B {
short m_c;
};

int main() {
C c1 { 1, 2, 3, 4 };
B& b1 = c1;
B b2 { 5, 6, 7 };

printf("before operator=: %dn", int(c1.m_c)); // 4
b1 = b2;
printf("after operator=: %dn", int(c1.m_c)); // 4

printf("before std::copy: %dn", int(c1.m_c)); // 4
std::copy(&b2, &b2 + 1, &b1);
printf("after std::copy: %dn", int(c1.m_c)); // 64, or 0, or anything but 4
}



Here, = does the right thing (it does not override B's tail padding), but copy() has a library optimization that reduces to memmove() - which does not care about tail padding because it assumes it does not exist.






share|improve this answer






























    up vote
    1
    down vote













    FooBefore derived;
    FooBeforeBase src, &dst=derived;
    ....
    memcpy(&dst, &src, sizeof(dst));


    If the additional data member was placed in the hole, memcpy would have overwritten it.



    As is correctly pointed out in comments, the standard doesn't require that this memcpy invocation should work. However the Itanium ABI is seemingly designed with this case in mind. Perhaps the ABI rules are specified this way in order to make mixed-language programming a bit more robust, or to preserve some kind of backwards compatibility.



    Relevant ABI rules can be found here.



    A related answer can be found here (this question might be a duplicate of that one).






    share|improve this answer



















    • 1




      TriviallyCopyable does not work if you try to copy into a base class subobject.
      – Nicol Bolas
      4 hours ago










    • The next question: why does the Itanium ABI designed this way? :)
      – geza
      4 hours ago










    • @geza I don't know but my guess is up there in the answer.
      – n.m.
      3 hours ago


















    up vote
    0
    down vote













    Here is a concrete case which demonstrates why the second case cannot reuse the padding:



    union bob {
    FooBeforeBase a;
    FooBefore b;
    };

    bob.b.value = 3.14;
    memset( &bob.a, 0, sizeof(bob.a) );


    this cannot clear bob.b.value.



    union bob2 {
    FooAfterBase a;
    FooAfter b;
    };

    bob2.b.value = 3.14;
    memset( &bob2.a, 0, sizeof(bob2.a) );


    this is undefined behavior.






    share|improve this answer





















    • "this cannot clear bob.b.value." Since FooBefore is not standard layout, the common-initial-sequence rule doesn't apply. So you can't access bob.a after you've set bob.b.
      – Nicol Bolas
      5 hours ago










    • @NicolBolas Why would FooBefore not be standard layout?
      – Holt
      5 hours ago








    • 3




      @Holt: both FooBeforeBase and FooBefore has non-static members, therefore FooBefore has no standard layout.
      – geza
      4 hours ago






    • 1




      @Holt A standard layout type has data members either in a base class or not in a base class, but not both. en.cppreference.com/w/cpp/named_req/StandardLayoutType
      – n.m.
      4 hours ago






    • 1




      @Holt: It's the "has no element of the set" part. That's what it says once you untangle all of the spec-language.
      – Nicol Bolas
      4 hours ago


















    up vote
    0
    down vote













    FooBefore is not std-layout either; two classes are declaring none-static data members(FooBefore and FooBeforeBase). Thus the compiler is allowed to arbitrarily place some data members. Hence the differences on different tool-chains arise.
    In a std-layout hierarchy, atmost one class(either the most derived class or at most one intermediate class) shall declare none-static data members.






    share|improve this answer




























      up vote
      -1
      down vote













      Here's a similar case as n.m.'s answer.



      First, let's have a function, which clears a FooBeforeBase:



      void clearBase(FooBeforeBase *f) {
      memset(f, 0, sizeof(*f));
      }


      This is fine, as clearBase gets a pointer to FooBeforeBase, it thinks that as FooBeforeBase has standard-layout, so memsetting it is safe.



      Now, if you do this:



      FooBefore b;
      b.value = 42;
      clearBase(&b);


      You don't expect, that clearBase will clear b.value, as b.value is not part of FooBeforeBase. But, if FooBefore::value was put into tail-padding of FooBeforeBase, it would been cleared as well.




      Is there a required layout per the standard and, if not, why do gcc and clang do what they do?




      No, tail-padding is not required. It is an optimization, which gcc and clang do.






      share|improve this answer





















      • But the standard doesn't allow clearBase to work on a base class subobject. Well, if we're going to be technical, memset isn't allowed on TriviallyCopyable types period, but even if that were a memcpy from a zero-initialized FooBeforeBase static instance, it still wouldn't be allowed on base class subobjects.
        – Nicol Bolas
        4 hours ago












      • @NicolBolas: I nowhere stated that. As a user of clearBase, you may not know, what is inside. So, this behavior of the compiler guarantees that you don't shoot yourself in the foot. Please be a little bit more practical here, even if the question has the language-lawyer tag. We already talking about something, which is not covered by the standard (i.e., tail-padding optimization).
        – geza
        4 hours ago












      • "I nowhere stated that". You very much did, right after you said: "Now, if you do this:". That's code which calls clearBase on a base class subobject, which is UB.
        – Nicol Bolas
        4 hours ago








      • 1




        "But the standard doesn't allow clearBase to work" Rather, it doesn't guarantee that it will work.
        – n.m.
        4 hours ago










      • @NicolBolas: okay then. This behavior is there too guarantee, that even if it is UB, it doesn't do any harm. UB doesn't automatically mean that something bad must happen.
        – geza
        4 hours ago











      Your Answer






      StackExchange.ifUsing("editor", function () {
      StackExchange.using("externalEditor", function () {
      StackExchange.using("snippets", function () {
      StackExchange.snippets.init();
      });
      });
      }, "code-snippets");

      StackExchange.ready(function() {
      var channelOptions = {
      tags: "".split(" "),
      id: "1"
      };
      initTagRenderer("".split(" "), "".split(" "), channelOptions);

      StackExchange.using("externalEditor", function() {
      // Have to fire editor after snippets, if snippets enabled
      if (StackExchange.settings.snippets.snippetsEnabled) {
      StackExchange.using("snippets", function() {
      createEditor();
      });
      }
      else {
      createEditor();
      }
      });

      function createEditor() {
      StackExchange.prepareEditor({
      heartbeatType: 'answer',
      convertImagesToLinks: true,
      noModals: true,
      showLowRepImageUploadWarning: true,
      reputationToPostImages: 10,
      bindNavPrevention: true,
      postfix: "",
      imageUploader: {
      brandingHtml: "Powered by u003ca class="icon-imgur-white" href="https://imgur.com/"u003eu003c/au003e",
      contentPolicyHtml: "User contributions licensed under u003ca href="https://creativecommons.org/licenses/by-sa/3.0/"u003ecc by-sa 3.0 with attribution requiredu003c/au003e u003ca href="https://stackoverflow.com/legal/content-policy"u003e(content policy)u003c/au003e",
      allowUrls: true
      },
      onDemand: true,
      discardSelector: ".discard-answer"
      ,immediatelyShowMarkdownHelp:true
      });


      }
      });














      draft saved

      draft discarded


















      StackExchange.ready(
      function () {
      StackExchange.openid.initPostLogin('.new-post-login', 'https%3a%2f%2fstackoverflow.com%2fquestions%2f53837373%2fstandard-layout-and-tail-padding%23new-answer', 'question_page');
      }
      );

      Post as a guest















      Required, but never shown

























      5 Answers
      5






      active

      oldest

      votes








      5 Answers
      5






      active

      oldest

      votes









      active

      oldest

      votes






      active

      oldest

      votes








      up vote
      3
      down vote













      The answer to this question doesn't come from the standard but rather from the Itanium ABI (which is why gcc and clang have one behavior but msvc does something else). That ABI defines a layout, the relevant parts of which for the purposes of this question are:




      For purposes internal to the specification, we also specify:





      • dsize(O): the data size of an object, which is the size of O without tail padding.




      and




      We ignore tail padding for PODs because an early version of the standard did not allow us to use it for anything else and because it sometimes permits faster copying of the type.




      Where the placement of members other than virtual base classes is defined as:




      Start at offset dsize(C), incremented if necessary for alignment to nvalign(D) for base classes or to align(D) for data members. Place D at this offset unless [... not relevant ...].




      The term POD has disappeared from the C++ standard, but it means standard-layout and trivially copyable. In this question, FooBeforeBase is a POD. The Itanium ABI ignores tail padding - hence dsize(FooBeforeBase) is 16.



      But FooAfterBase is not a POD (it is trivially copyable, but it is not standard-layout). As a result, tail padding is not ignored, so dsize(FooAfterBase) is just 12, and the float can go right there.



      This has interesting consequences, as pointed out by Quuxplusone in a related answer, implementors also typically assume that tail padding isn't reused, which wreaks havoc on this example:




      #include <algorithm>
      #include <stdio.h>

      struct A {
      int m_a;
      };

      struct B : A {
      int m_b1;
      char m_b2;
      };

      struct C : B {
      short m_c;
      };

      int main() {
      C c1 { 1, 2, 3, 4 };
      B& b1 = c1;
      B b2 { 5, 6, 7 };

      printf("before operator=: %dn", int(c1.m_c)); // 4
      b1 = b2;
      printf("after operator=: %dn", int(c1.m_c)); // 4

      printf("before std::copy: %dn", int(c1.m_c)); // 4
      std::copy(&b2, &b2 + 1, &b1);
      printf("after std::copy: %dn", int(c1.m_c)); // 64, or 0, or anything but 4
      }



      Here, = does the right thing (it does not override B's tail padding), but copy() has a library optimization that reduces to memmove() - which does not care about tail padding because it assumes it does not exist.






      share|improve this answer



























        up vote
        3
        down vote













        The answer to this question doesn't come from the standard but rather from the Itanium ABI (which is why gcc and clang have one behavior but msvc does something else). That ABI defines a layout, the relevant parts of which for the purposes of this question are:




        For purposes internal to the specification, we also specify:





        • dsize(O): the data size of an object, which is the size of O without tail padding.




        and




        We ignore tail padding for PODs because an early version of the standard did not allow us to use it for anything else and because it sometimes permits faster copying of the type.




        Where the placement of members other than virtual base classes is defined as:




        Start at offset dsize(C), incremented if necessary for alignment to nvalign(D) for base classes or to align(D) for data members. Place D at this offset unless [... not relevant ...].




        The term POD has disappeared from the C++ standard, but it means standard-layout and trivially copyable. In this question, FooBeforeBase is a POD. The Itanium ABI ignores tail padding - hence dsize(FooBeforeBase) is 16.



        But FooAfterBase is not a POD (it is trivially copyable, but it is not standard-layout). As a result, tail padding is not ignored, so dsize(FooAfterBase) is just 12, and the float can go right there.



        This has interesting consequences, as pointed out by Quuxplusone in a related answer, implementors also typically assume that tail padding isn't reused, which wreaks havoc on this example:




        #include <algorithm>
        #include <stdio.h>

        struct A {
        int m_a;
        };

        struct B : A {
        int m_b1;
        char m_b2;
        };

        struct C : B {
        short m_c;
        };

        int main() {
        C c1 { 1, 2, 3, 4 };
        B& b1 = c1;
        B b2 { 5, 6, 7 };

        printf("before operator=: %dn", int(c1.m_c)); // 4
        b1 = b2;
        printf("after operator=: %dn", int(c1.m_c)); // 4

        printf("before std::copy: %dn", int(c1.m_c)); // 4
        std::copy(&b2, &b2 + 1, &b1);
        printf("after std::copy: %dn", int(c1.m_c)); // 64, or 0, or anything but 4
        }



        Here, = does the right thing (it does not override B's tail padding), but copy() has a library optimization that reduces to memmove() - which does not care about tail padding because it assumes it does not exist.






        share|improve this answer

























          up vote
          3
          down vote










          up vote
          3
          down vote









          The answer to this question doesn't come from the standard but rather from the Itanium ABI (which is why gcc and clang have one behavior but msvc does something else). That ABI defines a layout, the relevant parts of which for the purposes of this question are:




          For purposes internal to the specification, we also specify:





          • dsize(O): the data size of an object, which is the size of O without tail padding.




          and




          We ignore tail padding for PODs because an early version of the standard did not allow us to use it for anything else and because it sometimes permits faster copying of the type.




          Where the placement of members other than virtual base classes is defined as:




          Start at offset dsize(C), incremented if necessary for alignment to nvalign(D) for base classes or to align(D) for data members. Place D at this offset unless [... not relevant ...].




          The term POD has disappeared from the C++ standard, but it means standard-layout and trivially copyable. In this question, FooBeforeBase is a POD. The Itanium ABI ignores tail padding - hence dsize(FooBeforeBase) is 16.



          But FooAfterBase is not a POD (it is trivially copyable, but it is not standard-layout). As a result, tail padding is not ignored, so dsize(FooAfterBase) is just 12, and the float can go right there.



          This has interesting consequences, as pointed out by Quuxplusone in a related answer, implementors also typically assume that tail padding isn't reused, which wreaks havoc on this example:




          #include <algorithm>
          #include <stdio.h>

          struct A {
          int m_a;
          };

          struct B : A {
          int m_b1;
          char m_b2;
          };

          struct C : B {
          short m_c;
          };

          int main() {
          C c1 { 1, 2, 3, 4 };
          B& b1 = c1;
          B b2 { 5, 6, 7 };

          printf("before operator=: %dn", int(c1.m_c)); // 4
          b1 = b2;
          printf("after operator=: %dn", int(c1.m_c)); // 4

          printf("before std::copy: %dn", int(c1.m_c)); // 4
          std::copy(&b2, &b2 + 1, &b1);
          printf("after std::copy: %dn", int(c1.m_c)); // 64, or 0, or anything but 4
          }



          Here, = does the right thing (it does not override B's tail padding), but copy() has a library optimization that reduces to memmove() - which does not care about tail padding because it assumes it does not exist.






          share|improve this answer














          The answer to this question doesn't come from the standard but rather from the Itanium ABI (which is why gcc and clang have one behavior but msvc does something else). That ABI defines a layout, the relevant parts of which for the purposes of this question are:




          For purposes internal to the specification, we also specify:





          • dsize(O): the data size of an object, which is the size of O without tail padding.




          and




          We ignore tail padding for PODs because an early version of the standard did not allow us to use it for anything else and because it sometimes permits faster copying of the type.




          Where the placement of members other than virtual base classes is defined as:




          Start at offset dsize(C), incremented if necessary for alignment to nvalign(D) for base classes or to align(D) for data members. Place D at this offset unless [... not relevant ...].




          The term POD has disappeared from the C++ standard, but it means standard-layout and trivially copyable. In this question, FooBeforeBase is a POD. The Itanium ABI ignores tail padding - hence dsize(FooBeforeBase) is 16.



          But FooAfterBase is not a POD (it is trivially copyable, but it is not standard-layout). As a result, tail padding is not ignored, so dsize(FooAfterBase) is just 12, and the float can go right there.



          This has interesting consequences, as pointed out by Quuxplusone in a related answer, implementors also typically assume that tail padding isn't reused, which wreaks havoc on this example:




          #include <algorithm>
          #include <stdio.h>

          struct A {
          int m_a;
          };

          struct B : A {
          int m_b1;
          char m_b2;
          };

          struct C : B {
          short m_c;
          };

          int main() {
          C c1 { 1, 2, 3, 4 };
          B& b1 = c1;
          B b2 { 5, 6, 7 };

          printf("before operator=: %dn", int(c1.m_c)); // 4
          b1 = b2;
          printf("after operator=: %dn", int(c1.m_c)); // 4

          printf("before std::copy: %dn", int(c1.m_c)); // 4
          std::copy(&b2, &b2 + 1, &b1);
          printf("after std::copy: %dn", int(c1.m_c)); // 64, or 0, or anything but 4
          }



          Here, = does the right thing (it does not override B's tail padding), but copy() has a library optimization that reduces to memmove() - which does not care about tail padding because it assumes it does not exist.







          share|improve this answer














          share|improve this answer



          share|improve this answer








          answered 3 hours ago


























          community wiki





          Barry

























              up vote
              1
              down vote













              FooBefore derived;
              FooBeforeBase src, &dst=derived;
              ....
              memcpy(&dst, &src, sizeof(dst));


              If the additional data member was placed in the hole, memcpy would have overwritten it.



              As is correctly pointed out in comments, the standard doesn't require that this memcpy invocation should work. However the Itanium ABI is seemingly designed with this case in mind. Perhaps the ABI rules are specified this way in order to make mixed-language programming a bit more robust, or to preserve some kind of backwards compatibility.



              Relevant ABI rules can be found here.



              A related answer can be found here (this question might be a duplicate of that one).






              share|improve this answer



















              • 1




                TriviallyCopyable does not work if you try to copy into a base class subobject.
                – Nicol Bolas
                4 hours ago










              • The next question: why does the Itanium ABI designed this way? :)
                – geza
                4 hours ago










              • @geza I don't know but my guess is up there in the answer.
                – n.m.
                3 hours ago















              up vote
              1
              down vote













              FooBefore derived;
              FooBeforeBase src, &dst=derived;
              ....
              memcpy(&dst, &src, sizeof(dst));


              If the additional data member was placed in the hole, memcpy would have overwritten it.



              As is correctly pointed out in comments, the standard doesn't require that this memcpy invocation should work. However the Itanium ABI is seemingly designed with this case in mind. Perhaps the ABI rules are specified this way in order to make mixed-language programming a bit more robust, or to preserve some kind of backwards compatibility.



              Relevant ABI rules can be found here.



              A related answer can be found here (this question might be a duplicate of that one).






              share|improve this answer



















              • 1




                TriviallyCopyable does not work if you try to copy into a base class subobject.
                – Nicol Bolas
                4 hours ago










              • The next question: why does the Itanium ABI designed this way? :)
                – geza
                4 hours ago










              • @geza I don't know but my guess is up there in the answer.
                – n.m.
                3 hours ago













              up vote
              1
              down vote










              up vote
              1
              down vote









              FooBefore derived;
              FooBeforeBase src, &dst=derived;
              ....
              memcpy(&dst, &src, sizeof(dst));


              If the additional data member was placed in the hole, memcpy would have overwritten it.



              As is correctly pointed out in comments, the standard doesn't require that this memcpy invocation should work. However the Itanium ABI is seemingly designed with this case in mind. Perhaps the ABI rules are specified this way in order to make mixed-language programming a bit more robust, or to preserve some kind of backwards compatibility.



              Relevant ABI rules can be found here.



              A related answer can be found here (this question might be a duplicate of that one).






              share|improve this answer














              FooBefore derived;
              FooBeforeBase src, &dst=derived;
              ....
              memcpy(&dst, &src, sizeof(dst));


              If the additional data member was placed in the hole, memcpy would have overwritten it.



              As is correctly pointed out in comments, the standard doesn't require that this memcpy invocation should work. However the Itanium ABI is seemingly designed with this case in mind. Perhaps the ABI rules are specified this way in order to make mixed-language programming a bit more robust, or to preserve some kind of backwards compatibility.



              Relevant ABI rules can be found here.



              A related answer can be found here (this question might be a duplicate of that one).







              share|improve this answer














              share|improve this answer



              share|improve this answer








              edited 3 hours ago

























              answered 4 hours ago









              n.m.

              70.9k882166




              70.9k882166








              • 1




                TriviallyCopyable does not work if you try to copy into a base class subobject.
                – Nicol Bolas
                4 hours ago










              • The next question: why does the Itanium ABI designed this way? :)
                – geza
                4 hours ago










              • @geza I don't know but my guess is up there in the answer.
                – n.m.
                3 hours ago














              • 1




                TriviallyCopyable does not work if you try to copy into a base class subobject.
                – Nicol Bolas
                4 hours ago










              • The next question: why does the Itanium ABI designed this way? :)
                – geza
                4 hours ago










              • @geza I don't know but my guess is up there in the answer.
                – n.m.
                3 hours ago








              1




              1




              TriviallyCopyable does not work if you try to copy into a base class subobject.
              – Nicol Bolas
              4 hours ago




              TriviallyCopyable does not work if you try to copy into a base class subobject.
              – Nicol Bolas
              4 hours ago












              The next question: why does the Itanium ABI designed this way? :)
              – geza
              4 hours ago




              The next question: why does the Itanium ABI designed this way? :)
              – geza
              4 hours ago












              @geza I don't know but my guess is up there in the answer.
              – n.m.
              3 hours ago




              @geza I don't know but my guess is up there in the answer.
              – n.m.
              3 hours ago










              up vote
              0
              down vote













              Here is a concrete case which demonstrates why the second case cannot reuse the padding:



              union bob {
              FooBeforeBase a;
              FooBefore b;
              };

              bob.b.value = 3.14;
              memset( &bob.a, 0, sizeof(bob.a) );


              this cannot clear bob.b.value.



              union bob2 {
              FooAfterBase a;
              FooAfter b;
              };

              bob2.b.value = 3.14;
              memset( &bob2.a, 0, sizeof(bob2.a) );


              this is undefined behavior.






              share|improve this answer





















              • "this cannot clear bob.b.value." Since FooBefore is not standard layout, the common-initial-sequence rule doesn't apply. So you can't access bob.a after you've set bob.b.
                – Nicol Bolas
                5 hours ago










              • @NicolBolas Why would FooBefore not be standard layout?
                – Holt
                5 hours ago








              • 3




                @Holt: both FooBeforeBase and FooBefore has non-static members, therefore FooBefore has no standard layout.
                – geza
                4 hours ago






              • 1




                @Holt A standard layout type has data members either in a base class or not in a base class, but not both. en.cppreference.com/w/cpp/named_req/StandardLayoutType
                – n.m.
                4 hours ago






              • 1




                @Holt: It's the "has no element of the set" part. That's what it says once you untangle all of the spec-language.
                – Nicol Bolas
                4 hours ago















              up vote
              0
              down vote













              Here is a concrete case which demonstrates why the second case cannot reuse the padding:



              union bob {
              FooBeforeBase a;
              FooBefore b;
              };

              bob.b.value = 3.14;
              memset( &bob.a, 0, sizeof(bob.a) );


              this cannot clear bob.b.value.



              union bob2 {
              FooAfterBase a;
              FooAfter b;
              };

              bob2.b.value = 3.14;
              memset( &bob2.a, 0, sizeof(bob2.a) );


              this is undefined behavior.






              share|improve this answer





















              • "this cannot clear bob.b.value." Since FooBefore is not standard layout, the common-initial-sequence rule doesn't apply. So you can't access bob.a after you've set bob.b.
                – Nicol Bolas
                5 hours ago










              • @NicolBolas Why would FooBefore not be standard layout?
                – Holt
                5 hours ago








              • 3




                @Holt: both FooBeforeBase and FooBefore has non-static members, therefore FooBefore has no standard layout.
                – geza
                4 hours ago






              • 1




                @Holt A standard layout type has data members either in a base class or not in a base class, but not both. en.cppreference.com/w/cpp/named_req/StandardLayoutType
                – n.m.
                4 hours ago






              • 1




                @Holt: It's the "has no element of the set" part. That's what it says once you untangle all of the spec-language.
                – Nicol Bolas
                4 hours ago













              up vote
              0
              down vote










              up vote
              0
              down vote









              Here is a concrete case which demonstrates why the second case cannot reuse the padding:



              union bob {
              FooBeforeBase a;
              FooBefore b;
              };

              bob.b.value = 3.14;
              memset( &bob.a, 0, sizeof(bob.a) );


              this cannot clear bob.b.value.



              union bob2 {
              FooAfterBase a;
              FooAfter b;
              };

              bob2.b.value = 3.14;
              memset( &bob2.a, 0, sizeof(bob2.a) );


              this is undefined behavior.






              share|improve this answer












              Here is a concrete case which demonstrates why the second case cannot reuse the padding:



              union bob {
              FooBeforeBase a;
              FooBefore b;
              };

              bob.b.value = 3.14;
              memset( &bob.a, 0, sizeof(bob.a) );


              this cannot clear bob.b.value.



              union bob2 {
              FooAfterBase a;
              FooAfter b;
              };

              bob2.b.value = 3.14;
              memset( &bob2.a, 0, sizeof(bob2.a) );


              this is undefined behavior.







              share|improve this answer












              share|improve this answer



              share|improve this answer










              answered 5 hours ago









              Yakk - Adam Nevraumont

              181k19188368




              181k19188368












              • "this cannot clear bob.b.value." Since FooBefore is not standard layout, the common-initial-sequence rule doesn't apply. So you can't access bob.a after you've set bob.b.
                – Nicol Bolas
                5 hours ago










              • @NicolBolas Why would FooBefore not be standard layout?
                – Holt
                5 hours ago








              • 3




                @Holt: both FooBeforeBase and FooBefore has non-static members, therefore FooBefore has no standard layout.
                – geza
                4 hours ago






              • 1




                @Holt A standard layout type has data members either in a base class or not in a base class, but not both. en.cppreference.com/w/cpp/named_req/StandardLayoutType
                – n.m.
                4 hours ago






              • 1




                @Holt: It's the "has no element of the set" part. That's what it says once you untangle all of the spec-language.
                – Nicol Bolas
                4 hours ago


















              • "this cannot clear bob.b.value." Since FooBefore is not standard layout, the common-initial-sequence rule doesn't apply. So you can't access bob.a after you've set bob.b.
                – Nicol Bolas
                5 hours ago










              • @NicolBolas Why would FooBefore not be standard layout?
                – Holt
                5 hours ago








              • 3




                @Holt: both FooBeforeBase and FooBefore has non-static members, therefore FooBefore has no standard layout.
                – geza
                4 hours ago






              • 1




                @Holt A standard layout type has data members either in a base class or not in a base class, but not both. en.cppreference.com/w/cpp/named_req/StandardLayoutType
                – n.m.
                4 hours ago






              • 1




                @Holt: It's the "has no element of the set" part. That's what it says once you untangle all of the spec-language.
                – Nicol Bolas
                4 hours ago
















              "this cannot clear bob.b.value." Since FooBefore is not standard layout, the common-initial-sequence rule doesn't apply. So you can't access bob.a after you've set bob.b.
              – Nicol Bolas
              5 hours ago




              "this cannot clear bob.b.value." Since FooBefore is not standard layout, the common-initial-sequence rule doesn't apply. So you can't access bob.a after you've set bob.b.
              – Nicol Bolas
              5 hours ago












              @NicolBolas Why would FooBefore not be standard layout?
              – Holt
              5 hours ago






              @NicolBolas Why would FooBefore not be standard layout?
              – Holt
              5 hours ago






              3




              3




              @Holt: both FooBeforeBase and FooBefore has non-static members, therefore FooBefore has no standard layout.
              – geza
              4 hours ago




              @Holt: both FooBeforeBase and FooBefore has non-static members, therefore FooBefore has no standard layout.
              – geza
              4 hours ago




              1




              1




              @Holt A standard layout type has data members either in a base class or not in a base class, but not both. en.cppreference.com/w/cpp/named_req/StandardLayoutType
              – n.m.
              4 hours ago




              @Holt A standard layout type has data members either in a base class or not in a base class, but not both. en.cppreference.com/w/cpp/named_req/StandardLayoutType
              – n.m.
              4 hours ago




              1




              1




              @Holt: It's the "has no element of the set" part. That's what it says once you untangle all of the spec-language.
              – Nicol Bolas
              4 hours ago




              @Holt: It's the "has no element of the set" part. That's what it says once you untangle all of the spec-language.
              – Nicol Bolas
              4 hours ago










              up vote
              0
              down vote













              FooBefore is not std-layout either; two classes are declaring none-static data members(FooBefore and FooBeforeBase). Thus the compiler is allowed to arbitrarily place some data members. Hence the differences on different tool-chains arise.
              In a std-layout hierarchy, atmost one class(either the most derived class or at most one intermediate class) shall declare none-static data members.






              share|improve this answer

























                up vote
                0
                down vote













                FooBefore is not std-layout either; two classes are declaring none-static data members(FooBefore and FooBeforeBase). Thus the compiler is allowed to arbitrarily place some data members. Hence the differences on different tool-chains arise.
                In a std-layout hierarchy, atmost one class(either the most derived class or at most one intermediate class) shall declare none-static data members.






                share|improve this answer























                  up vote
                  0
                  down vote










                  up vote
                  0
                  down vote









                  FooBefore is not std-layout either; two classes are declaring none-static data members(FooBefore and FooBeforeBase). Thus the compiler is allowed to arbitrarily place some data members. Hence the differences on different tool-chains arise.
                  In a std-layout hierarchy, atmost one class(either the most derived class or at most one intermediate class) shall declare none-static data members.






                  share|improve this answer












                  FooBefore is not std-layout either; two classes are declaring none-static data members(FooBefore and FooBeforeBase). Thus the compiler is allowed to arbitrarily place some data members. Hence the differences on different tool-chains arise.
                  In a std-layout hierarchy, atmost one class(either the most derived class or at most one intermediate class) shall declare none-static data members.







                  share|improve this answer












                  share|improve this answer



                  share|improve this answer










                  answered 3 hours ago









                  Red.Wave

                  71937




                  71937






















                      up vote
                      -1
                      down vote













                      Here's a similar case as n.m.'s answer.



                      First, let's have a function, which clears a FooBeforeBase:



                      void clearBase(FooBeforeBase *f) {
                      memset(f, 0, sizeof(*f));
                      }


                      This is fine, as clearBase gets a pointer to FooBeforeBase, it thinks that as FooBeforeBase has standard-layout, so memsetting it is safe.



                      Now, if you do this:



                      FooBefore b;
                      b.value = 42;
                      clearBase(&b);


                      You don't expect, that clearBase will clear b.value, as b.value is not part of FooBeforeBase. But, if FooBefore::value was put into tail-padding of FooBeforeBase, it would been cleared as well.




                      Is there a required layout per the standard and, if not, why do gcc and clang do what they do?




                      No, tail-padding is not required. It is an optimization, which gcc and clang do.






                      share|improve this answer





















                      • But the standard doesn't allow clearBase to work on a base class subobject. Well, if we're going to be technical, memset isn't allowed on TriviallyCopyable types period, but even if that were a memcpy from a zero-initialized FooBeforeBase static instance, it still wouldn't be allowed on base class subobjects.
                        – Nicol Bolas
                        4 hours ago












                      • @NicolBolas: I nowhere stated that. As a user of clearBase, you may not know, what is inside. So, this behavior of the compiler guarantees that you don't shoot yourself in the foot. Please be a little bit more practical here, even if the question has the language-lawyer tag. We already talking about something, which is not covered by the standard (i.e., tail-padding optimization).
                        – geza
                        4 hours ago












                      • "I nowhere stated that". You very much did, right after you said: "Now, if you do this:". That's code which calls clearBase on a base class subobject, which is UB.
                        – Nicol Bolas
                        4 hours ago








                      • 1




                        "But the standard doesn't allow clearBase to work" Rather, it doesn't guarantee that it will work.
                        – n.m.
                        4 hours ago










                      • @NicolBolas: okay then. This behavior is there too guarantee, that even if it is UB, it doesn't do any harm. UB doesn't automatically mean that something bad must happen.
                        – geza
                        4 hours ago















                      up vote
                      -1
                      down vote













                      Here's a similar case as n.m.'s answer.



                      First, let's have a function, which clears a FooBeforeBase:



                      void clearBase(FooBeforeBase *f) {
                      memset(f, 0, sizeof(*f));
                      }


                      This is fine, as clearBase gets a pointer to FooBeforeBase, it thinks that as FooBeforeBase has standard-layout, so memsetting it is safe.



                      Now, if you do this:



                      FooBefore b;
                      b.value = 42;
                      clearBase(&b);


                      You don't expect, that clearBase will clear b.value, as b.value is not part of FooBeforeBase. But, if FooBefore::value was put into tail-padding of FooBeforeBase, it would been cleared as well.




                      Is there a required layout per the standard and, if not, why do gcc and clang do what they do?




                      No, tail-padding is not required. It is an optimization, which gcc and clang do.






                      share|improve this answer





















                      • But the standard doesn't allow clearBase to work on a base class subobject. Well, if we're going to be technical, memset isn't allowed on TriviallyCopyable types period, but even if that were a memcpy from a zero-initialized FooBeforeBase static instance, it still wouldn't be allowed on base class subobjects.
                        – Nicol Bolas
                        4 hours ago












                      • @NicolBolas: I nowhere stated that. As a user of clearBase, you may not know, what is inside. So, this behavior of the compiler guarantees that you don't shoot yourself in the foot. Please be a little bit more practical here, even if the question has the language-lawyer tag. We already talking about something, which is not covered by the standard (i.e., tail-padding optimization).
                        – geza
                        4 hours ago












                      • "I nowhere stated that". You very much did, right after you said: "Now, if you do this:". That's code which calls clearBase on a base class subobject, which is UB.
                        – Nicol Bolas
                        4 hours ago








                      • 1




                        "But the standard doesn't allow clearBase to work" Rather, it doesn't guarantee that it will work.
                        – n.m.
                        4 hours ago










                      • @NicolBolas: okay then. This behavior is there too guarantee, that even if it is UB, it doesn't do any harm. UB doesn't automatically mean that something bad must happen.
                        – geza
                        4 hours ago













                      up vote
                      -1
                      down vote










                      up vote
                      -1
                      down vote









                      Here's a similar case as n.m.'s answer.



                      First, let's have a function, which clears a FooBeforeBase:



                      void clearBase(FooBeforeBase *f) {
                      memset(f, 0, sizeof(*f));
                      }


                      This is fine, as clearBase gets a pointer to FooBeforeBase, it thinks that as FooBeforeBase has standard-layout, so memsetting it is safe.



                      Now, if you do this:



                      FooBefore b;
                      b.value = 42;
                      clearBase(&b);


                      You don't expect, that clearBase will clear b.value, as b.value is not part of FooBeforeBase. But, if FooBefore::value was put into tail-padding of FooBeforeBase, it would been cleared as well.




                      Is there a required layout per the standard and, if not, why do gcc and clang do what they do?




                      No, tail-padding is not required. It is an optimization, which gcc and clang do.






                      share|improve this answer












                      Here's a similar case as n.m.'s answer.



                      First, let's have a function, which clears a FooBeforeBase:



                      void clearBase(FooBeforeBase *f) {
                      memset(f, 0, sizeof(*f));
                      }


                      This is fine, as clearBase gets a pointer to FooBeforeBase, it thinks that as FooBeforeBase has standard-layout, so memsetting it is safe.



                      Now, if you do this:



                      FooBefore b;
                      b.value = 42;
                      clearBase(&b);


                      You don't expect, that clearBase will clear b.value, as b.value is not part of FooBeforeBase. But, if FooBefore::value was put into tail-padding of FooBeforeBase, it would been cleared as well.




                      Is there a required layout per the standard and, if not, why do gcc and clang do what they do?




                      No, tail-padding is not required. It is an optimization, which gcc and clang do.







                      share|improve this answer












                      share|improve this answer



                      share|improve this answer










                      answered 4 hours ago









                      geza

                      12.5k32775




                      12.5k32775












                      • But the standard doesn't allow clearBase to work on a base class subobject. Well, if we're going to be technical, memset isn't allowed on TriviallyCopyable types period, but even if that were a memcpy from a zero-initialized FooBeforeBase static instance, it still wouldn't be allowed on base class subobjects.
                        – Nicol Bolas
                        4 hours ago












                      • @NicolBolas: I nowhere stated that. As a user of clearBase, you may not know, what is inside. So, this behavior of the compiler guarantees that you don't shoot yourself in the foot. Please be a little bit more practical here, even if the question has the language-lawyer tag. We already talking about something, which is not covered by the standard (i.e., tail-padding optimization).
                        – geza
                        4 hours ago












                      • "I nowhere stated that". You very much did, right after you said: "Now, if you do this:". That's code which calls clearBase on a base class subobject, which is UB.
                        – Nicol Bolas
                        4 hours ago








                      • 1




                        "But the standard doesn't allow clearBase to work" Rather, it doesn't guarantee that it will work.
                        – n.m.
                        4 hours ago










                      • @NicolBolas: okay then. This behavior is there too guarantee, that even if it is UB, it doesn't do any harm. UB doesn't automatically mean that something bad must happen.
                        – geza
                        4 hours ago


















                      • But the standard doesn't allow clearBase to work on a base class subobject. Well, if we're going to be technical, memset isn't allowed on TriviallyCopyable types period, but even if that were a memcpy from a zero-initialized FooBeforeBase static instance, it still wouldn't be allowed on base class subobjects.
                        – Nicol Bolas
                        4 hours ago












                      • @NicolBolas: I nowhere stated that. As a user of clearBase, you may not know, what is inside. So, this behavior of the compiler guarantees that you don't shoot yourself in the foot. Please be a little bit more practical here, even if the question has the language-lawyer tag. We already talking about something, which is not covered by the standard (i.e., tail-padding optimization).
                        – geza
                        4 hours ago












                      • "I nowhere stated that". You very much did, right after you said: "Now, if you do this:". That's code which calls clearBase on a base class subobject, which is UB.
                        – Nicol Bolas
                        4 hours ago








                      • 1




                        "But the standard doesn't allow clearBase to work" Rather, it doesn't guarantee that it will work.
                        – n.m.
                        4 hours ago










                      • @NicolBolas: okay then. This behavior is there too guarantee, that even if it is UB, it doesn't do any harm. UB doesn't automatically mean that something bad must happen.
                        – geza
                        4 hours ago
















                      But the standard doesn't allow clearBase to work on a base class subobject. Well, if we're going to be technical, memset isn't allowed on TriviallyCopyable types period, but even if that were a memcpy from a zero-initialized FooBeforeBase static instance, it still wouldn't be allowed on base class subobjects.
                      – Nicol Bolas
                      4 hours ago






                      But the standard doesn't allow clearBase to work on a base class subobject. Well, if we're going to be technical, memset isn't allowed on TriviallyCopyable types period, but even if that were a memcpy from a zero-initialized FooBeforeBase static instance, it still wouldn't be allowed on base class subobjects.
                      – Nicol Bolas
                      4 hours ago














                      @NicolBolas: I nowhere stated that. As a user of clearBase, you may not know, what is inside. So, this behavior of the compiler guarantees that you don't shoot yourself in the foot. Please be a little bit more practical here, even if the question has the language-lawyer tag. We already talking about something, which is not covered by the standard (i.e., tail-padding optimization).
                      – geza
                      4 hours ago






                      @NicolBolas: I nowhere stated that. As a user of clearBase, you may not know, what is inside. So, this behavior of the compiler guarantees that you don't shoot yourself in the foot. Please be a little bit more practical here, even if the question has the language-lawyer tag. We already talking about something, which is not covered by the standard (i.e., tail-padding optimization).
                      – geza
                      4 hours ago














                      "I nowhere stated that". You very much did, right after you said: "Now, if you do this:". That's code which calls clearBase on a base class subobject, which is UB.
                      – Nicol Bolas
                      4 hours ago






                      "I nowhere stated that". You very much did, right after you said: "Now, if you do this:". That's code which calls clearBase on a base class subobject, which is UB.
                      – Nicol Bolas
                      4 hours ago






                      1




                      1




                      "But the standard doesn't allow clearBase to work" Rather, it doesn't guarantee that it will work.
                      – n.m.
                      4 hours ago




                      "But the standard doesn't allow clearBase to work" Rather, it doesn't guarantee that it will work.
                      – n.m.
                      4 hours ago












                      @NicolBolas: okay then. This behavior is there too guarantee, that even if it is UB, it doesn't do any harm. UB doesn't automatically mean that something bad must happen.
                      – geza
                      4 hours ago




                      @NicolBolas: okay then. This behavior is there too guarantee, that even if it is UB, it doesn't do any harm. UB doesn't automatically mean that something bad must happen.
                      – geza
                      4 hours ago


















                      draft saved

                      draft discarded




















































                      Thanks for contributing an answer to Stack Overflow!


                      • Please be sure to answer the question. Provide details and share your research!

                      But avoid



                      • Asking for help, clarification, or responding to other answers.

                      • Making statements based on opinion; back them up with references or personal experience.


                      To learn more, see our tips on writing great answers.





                      Some of your past answers have not been well-received, and you're in danger of being blocked from answering.


                      Please pay close attention to the following guidance:


                      • Please be sure to answer the question. Provide details and share your research!

                      But avoid



                      • Asking for help, clarification, or responding to other answers.

                      • Making statements based on opinion; back them up with references or personal experience.


                      To learn more, see our tips on writing great answers.




                      draft saved


                      draft discarded














                      StackExchange.ready(
                      function () {
                      StackExchange.openid.initPostLogin('.new-post-login', 'https%3a%2f%2fstackoverflow.com%2fquestions%2f53837373%2fstandard-layout-and-tail-padding%23new-answer', 'question_page');
                      }
                      );

                      Post as a guest















                      Required, but never shown





















































                      Required, but never shown














                      Required, but never shown












                      Required, but never shown







                      Required, but never shown

































                      Required, but never shown














                      Required, but never shown












                      Required, but never shown







                      Required, but never shown







                      Popular posts from this blog

                      Feedback on college project

                      Futebolista

                      Albești (Vaslui)