lmi
[Top][All Lists]
Advanced

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

Re: [lmi] Detecting whether move semantics actually take place


From: Greg Chicares
Subject: Re: [lmi] Detecting whether move semantics actually take place
Date: Sun, 2 Oct 2022 23:25:37 +0000
User-agent: Mozilla/5.0 (X11; Linux x86_64; rv:102.0) Gecko/20100101 Thunderbird/102.3.0

On 7/28/22 14:08, Greg Chicares wrote:
> On 7/23/22 15:41, Vadim Zeitlin wrote:
>> On Thu, 21 Jul 2022 00:53:01 +0000 Greg Chicares <gchicares@sbcglobal.net> 
>> wrote:
>> 
>> GC> On 7/18/22 16:19, Vadim Zeitlin wrote:
>> GC> > On Mon, 18 Jul 2022 15:48:14 +0000 Greg Chicares 
>> <gchicares@sbcglobal.net> wrote:

[I had wondered whether it would be fruitful to write assertions like this...]

>   static_assert( lmi::is_effectively_copyable <A>);
>   static_assert( lmi::is_effectively_moveable <A>);
> 
> in order to handle this concern:
> 
> | >  One benefit that I hadn't mentioned yet, I think, is that if you ever
> | > define a special member in your class, e.g. decide to do something in the
> | > default ctor, it would silently inhibit moving. This should, arguably,
> | > happen only rarely, but I guarantee that if it does happen, it will remain
> | > unnoticed for a long time.
> |
> | Unless we guarantee that it cannot happen, e.g., with "concept" assertions.
> 
> so that, if we add an explicit dtor to class A, causing the move functions
> to become undeclared, then we'll get a compile-time assertion failure.
> 
> Thus, instead of adding five lines of special-member-function boilerplate
> to every class, we add one line at the end of each class definition, e.g.:
> 
>   class A : private lmi::polymorphic_base<A> {};
>   static_assert(std::semiregular<A> && 
> is_effectively_copyable_and_moveable<A>);
> 
> making the Rule of Zero safe.

With that in mind, I went back to older commits that had defined either
all five SMFs or none of them, e.g.:

$ git --no-pager log --oneline --grep='Rule of Zero' 
6a59da54 When in doubt, prefer the Rule of Five to the Rule of Zero
702c4cdd Follow the Rule of Zero (initializing all data members in class)
7d05361b Follow the Rule of Zero, even expunging a virtual dtor
601db178 Follow the Rule of Zero

and added such assertions, "making the Rule of Zero safe": see
new branch 'odd/concepts_smf'. I did that on a branch because I'm
not sure whether it's worth doing or not, and because it raises
some issues that I haven't grappled with yet. The case in favor of
doing this is:

>>  I.e. now we know that we can have is_effectively_moveable<T> (for
>> simplicity, I'm not distinguishing between move-constructible and
>> move-assignable neither here nor below), but how are you going to use it,
>> exactly? You could static_assert that it's true, but where would doing this
>> be useful?
> 
> When we want to use the Rule of Zero and make sure it doesn't invisibly
> break if we later add an explicit declaration. It's the invisibility
> that's a problem. In-your-face breakage--a static_assert() failure--is
> okay because we're forced to handle it immediately, before the breakage
> can spread.
[...]
>> GC> Suppose lmi copies an object, and I want to consider moving it instead.
>> GC> I can write the move operation, but will move semantics actually take
>> GC> place, or will the compiler silently substitute a copy to fulfill my
>> GC> move request? I don't simply want to make the request; I want to know
>> GC> how it's fulfilled. If moving is inherently impossible for some class,
>> GC> I'd like to know that before I waste time trying to move it.
>> 
>>  I think the simplest way of answering this question is to declare the move
>> constructor as default and rely on clang -Wdefaulted-function-deleted to
>> tell you if it turns out that your move ctor is implicitly deleted. Isn't
>> it?

Branch 'odd/concepts_smf' can be viewed as an alternative way,
which works with other compilers as well--and also works whether
the SMFs are implicit or explicit, as the following example shows:

> Here's a test case that gives no '-Wdefaulted-function-deleted'
> warning--in fact, if we comment out the assertion, clang compiles
> it successfully.
> 
> Copy the relevant parts from branch odd/move_semantics and add
> "constexpr" to equiplausible_construct(), then add:
> 
>   class E : public no_can_move {};
>   static_assert(equiplausible_construct<E>());
> 
> I think the difference between our test cases shows that clang
> diagnoses only the deletion of explicitly-declared special
> member functions. But E's SMFs are implicit: the compiler
> declares them, but AIUI does not define them because they
> aren't actually used; and, not having defined them, it doesn't
> need to delete them, so there's no warning.
> 
> And of course clang shouldn't routinely warn in cases like this,
> because for many classes in many projects the warning would be
> frequent and unwelcome. But for certain classes in lmi, I want
> to be warned, and the static assertion above does what I want.

This raises a broader question: using concepts, should we establish
a taxonomy for lmi classes, and statically assert that each of
these hundreds of classes fits its intended category? This goes far
beyond movability: for example, is it useful in general to assert
that a class is at least std::semiregular unless there's a good
reason for it not to be?

>> I'd like to mention a couple of improvements:
>> 
>>  First, and trivially, you should sprinkle magic constexpr dust all over

Done.

>>  Second, I think this would work even better with concepts rather than
>> simple template functions, both because the former are slightly more
>> readable (even if the functions here are pretty clear too) and because they
>> result in much better error messages, similar to the clang warning above,
>> except that this also works with gcc, if the requirement fails.

That raises another question: are concepts really all that useful
for classes, as opposed to the template parameters of class
templates?


reply via email to

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