lmi
[Top][All Lists]
Advanced

[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]

Re: [lmi] default-member-initializers vs. is_trivial


From: Greg Chicares
Subject: Re: [lmi] default-member-initializers vs. is_trivial
Date: Mon, 19 Apr 2021 20:57:23 +0000
User-agent: Mozilla/5.0 (X11; Linux x86_64; rv:78.0) Gecko/20100101 Thunderbird/78.9.0

On 4/18/21 11:09 PM, Vadim Zeitlin wrote:
> On Sun, 18 Apr 2021 22:50:29 +0000 Greg Chicares <gchicares@sbcglobal.net> 
> wrote:
> 
> GC> Vadim--Do you happen to know why default member initializers
> GC> prevent a default ctor from being trivial?
> 
>  Sorry if I'm missing something here, but for me "trivial" informally means
> "doing nothing" and, I believe, this is exactly what the C++ Standard says in
> a more formal way. I.e. a class with a trivial default ctor is like a POD C
> struct -- its ctor just does nothing at all.

Even if it constructs an instance by doing "nothing", it must at least
associate a name with a block of memory. Either that memory is initialized
in some way (e.g., all zeros), or not at all. As an experiment, I tried to
contrive a trivially-default-constructible struct that can't validly be
initialized with all zeros:

#include <type_traits>

enum E {e0 = 3, e1 = 17}; // none of the enumerated values is zero
struct S {E e;};

static_assert(std::is_trivially_constructible_v<S>);
static_assert(std::is_trivially_default_constructible_v<S>);
static_assert(std::is_trivially_copy_constructible_v<S>);
static_assert(std::is_trivially_move_constructible_v<S>);

int main(int, char*[])
{
    S s;
    std::cout << s.e << std::endl;
    return 0;
}

The static assertions succeed, but the stream-output line fails to compile:
  error: ‘s.S::e’ is used uninitialized

Then I tried
- struct S {E e;};
+ struct S {float e;}; // 'float' naturally defaults to 0.f

but compiling it with that modification again gives:
  ‘s.S::e’ is used uninitialized

It builds and runs with this modification:
-    std::cout << s.e << std::endl;
+    std::cout << sizeof(s.e) << std::endl;

Is this exactly what's expected? I suppose that's useful for...creating
a vector of objects that cannot be used because they're uninitialized?
Was that the goal? Why would such a goal be desirable? Surely I'm still
missing something.

>  And, clearly, if the ctor initializes its members -- as it has to do, if
> they do have initializers -- it must do something, and so can't be trivial.
> 
>  This reads very tautologically, but isn't it the point? I.e. it's not
> trivial because it can't do nothing while doing something.

I just wasn't expecting that.

> GC> a constexpr value could surely be allowed. For example, given
> GC>   struct foo{int i {1};};
> GC> shouldn't a compiler be able to default-construct a 'foo' quite easily?
> 
>  It probably can, but "easily" is not the same as "trivially".

Okay, my reasoning was that
  trivially copyable <--> implementable as memcpy()
so the underlying concept appeared to be
  trivially anything <--> translates to something like REPNE MOVSB
or IOW
  trivially XXX <--> implementable as memXXX()
from which it might follow that
  trivially constructible <-?-> implementable as memset()
but the Standard doesn't even allow memset() here.



reply via email to

[Prev in Thread] Current Thread [Next in Thread]