Indiana Unversity logo[ConceptGCC]

ConceptGCC :

Re: Shouldn't Assignable concepts stop supporting assignment-to-rvalue?

From: Sebastian Gesemann (s.gesemann_at_[hidden])
Date: 2009-01-07 10:40:12


On Wed, Jan 7, 2009 at 2:16 PM, Niels Dekker wrote:
> Sebastian Gesemann wrote:
>> I don't think that mistyping is a big issue. Adding ref qualifiers in
>> the STL would take functionality away. So, it's not backwards
>> compatible, strictly speaking.
> Thanks for your feedback, Sebastian. Of course, I'd rather have you support
> the idea of adding '&' ref-qualifiers to the assignment operators provided
> by the STL (whenever appropriate). But anyway, it's always good to hear a
> different opinion!

Just so we're clear: I have no authority. I'm just an observer like you.

>> I don't know whether this would really affect any code base, though.
> I'm very interested to hear any use case of assignment to an rvalue. So far,
> it only appears useful to me for very specific types, like proxy classes.
> For example, bitset::reference and vector<bool>::reference. slice_array,
> gslice_array, mask_array, and indirect_array have "const" assignment
> operators, so I don't think those should have a ref-qualifier either. I'm
> not entirely sure about the insert iterators [insert.iterators], though. Do
> you think it makes sense to assign a value of type Cont::value_type to a
> temporary back_insert_iterator<Cont>?

You wouldn't usually try to assign an object of type Cont::value_type
to an iterator without dereferencing it first. According to 24.4.2.1
[back.insert.iterator, N2800.pdf] the dereference operator returns an
lvalue reference (to the iterator itself but that's not important).
So, we're fine.

>> Anyhow, I'd like to see the ref qualifier in HasAssign<>:
>>
>> auto concept HasAssign<typename T, typename U>
>> typename result_type;
>> result_type T::operator=(U) &; // <-- added ref qualifier (not in
>> // draft)
>> }
>>
>> which would make the compiler reject your restricted function template
>> "assign_to_rvalue".
>
> Thanks! Would you also like to see the ref-qualifier in CopyAssignable and
> MoveAssignable?

CopyAssignable<T> and MoveAssignable<T> are refinements of
HasAssign<T,const T&> and HasAssign<T, T&&>. So, there would be no
need to change the other concepts as well.

Anyhow, I was wrong on the ref-qualifier. It is not necessary in
declarations for associated function requirements. Check out chapter
14.9.2.1 [concept.map.fct] that describes when such a function
requirement is satisfied. An associated function requirement of the
form

   result_type T::funcname(...);

is satisfied if the expression x.funcname(...) is implicitly
convertible to an object of type result_type, 'x' being an LVALUE.
(14.9.2.1/4) unless a &&-ref qualifier is used. For the sake of
brevity I ignored the function parameters. The only thing that this
concept requirement guarantees is that funcname(...) can be invoked on
lvalues. This renders the &-ref qualifier unnecessary. It also
implies that your ConceptGCC version failed to properly type-check
your constrained template.

>> The original version of this (N2800.pdf) obviously
>> requires rvalues to be assignable as well which renders built-in types
>> non-assignable as far as this concept is concerned.
> Are you sure? When I try ConceptGCC 4.3.0 alpha 7, HasAssign<T,T> (original
> version) appears to be satisfied when T is a built-in type:

You're right. My mistake. I was assuming different rules for
checking whether a member function requirement is satisfied. The
difference is that member functions without ref qualifiers can be
invoked on both, lvalues and rvalues. I thought that a function
requirement without any ref qualifier would be satisfied if the
expression "E" (see paper) is well-formed for both cases ('x' being an
rvalue and lvalue). But an associated member function requirement
without any ref qualifier only means that the member function can be
invoked on lvalues. So, in case I got that right the following should
be true:

   concept Test1<typename T> {
      void T::foo(); // requirement for lvalues only
   }

   concept Test2<typename T> {
      void T::foo(); // requirement for lvalues only
      void T::foo() &&; // requirement for rvalues only
   }

   struct A {
      void foo() &; // <- note ref qualifier
   };

   struct B {
      void foo();
   };

   concept_map Test1<A> {} // OK
   concept_map Test2<A> {} // Error
   concept_map Test1<B> {} // OK
   concept_map Test2<B> {} // OK

It certainly is a slight inconsistency since the meaning of the lack
of a ref qualifier depends on the context (function requirement versus
declaration).

My personal conclusion: The current standard draft is fine w.r.t. the
HasAssign<> and XxxAssignable<> concepts. Your ConceptGCC version is
buggy because these concepts don't support assignment to rvalues and
yet your constrained tempalte made use of it. ConceptGCC should have
rejected your constrained template "assign_to_rvalue". But the
inconsistency is bugging me. I see no problem in changing the
"satisfaction rules" for member function requirements so that the lack
of a ref qualifier should make the requirement satisfied only if BOTH
versions of the expression E ('x' being an lvalue in one case and an
rvalue in the other case) are well-formed. This modification would
necessitate the addition of an lvalue ref qualifier in function
requirements to keep the same semantics.

Cheers!
SG