Pizer’s Weblog

programming, DSP, math

C++0x: concepts & rvalue references — 2nd round

with 4 comments

Last update: 2009-02-1

Concepts and rvalue references are two new features that are going to be included in the next official version of the C++ language with high probability. I’m not going to mention the basics behind those two features because it has been done before in other articles. Try this google video or this article for an introduction to concepts by Doug Gregor and this article for an introduction to rvalue references by Howard Hinnant et al.

I already wrote a post on concepts and rvalue references here which discussed some problems when concepts are combined with rvalue references. It turned out that David Abrahams and Doug Gregor were also aware of the first issue I addressed. They published an article one day before I wrote about it. In this post I’m going to address other arguments (consistency and intuitivity) for why I think their proposed resolution is a good idea and what other language modification can increase consistency. Before I go into the last bit I will shed some light on what function requirements actually mean and how they can be satisfied.

Update 2009-02-1:[ It turns out you can't exprapolate the meaning of function requirements from the concept_map checking rules because they don't cover all use cases in constrained contexts (see hole in concepts system). So, keep this in mind while reading the rest of this article.]

Let’s recap: Under current rules an rvalue reference can bind to both rvalues and lvalues. This is likely to cause trouble in cases where we rely on overload resolution between & and && to pick the correct function when concept requirements or SFINAE is involved and a const& overload is removed silently. I'm in total agreement with the article by Abrahams and Gregor and their proposed resolution. Also, the current rules w.r.t. the meaning of reference types and reference qualifiers is slightly inconsistent, especially among different contexts including concepts. The proposal by Abrahams et al improves the situation in terms of consistency, too.

Reference types: & may bind to lvalues only. const& may also bind to rvalues. && is allowed to bind to both: rvalues and lvalues. The current proposal N2800 also includes an extension of the rvalue reference proposal to *this for member functions. However, it is not 100% consistent with respect to the meaning of && as used in the basic proposal. The ref qualifer & added after a member function declaration prevents the function from being callable on rvalues. The ref qualifier && prevents the member function from being callable on lvalues. In isolation this seems like a very reasonable way to go for member functions and ref qualifiers since the lack of a ref qualifier makes the function callable in both cases. But the meaning of && is obviously slightly different. The proposed resolution by Abrahams and Gregor -- making && generally bind only to rvalues -- would unify the meaning of && here. This is a good thing -- after all, they are called rvalue references. ;)

Reference types in concepts: The meaning of reference types and ref qualifiers for function requirements in concepts is special. A function requirement is not the same as a function prototype for which a function definition must exist that uses exactly the same signature. A function requirement is more easily satisfied. Think of function requirements as "invocation signatures". They define what kind of invocation expressions are guaranteed to be well-formed. Here is an example:

  concept HasFoo<typename T> {
    void foo(T);
  }

  long foo(long);

  concept_map HasFoo<int> {} // OK

You see that it's OK to have a function with a slightly different signature. The important thing is that the requirement only guarantees a specific invocation expression to be well-formed. An int can be implicitly converted to a long and the function's result is always "convertible" to void. Implicit conversion is not the only thing that might differ. The function foo could also have accepted a const& parameter as long as the invocation expression implied by the concept's function requirement is well-formed. In this case the expression is foo(p) where p is of type T. For T=int this expression is obviously well-formed because of long foo(long). Question: What do you think is the meaning of the following function requirement:

  concept HasFooRvalRef<typename T> {
    void foo(T&&);
  }

Under current rules a function that is declared like this accepts both: rvalues and lvalues. You know that by now. But in the context of concepts this is a function requirement. It is satisfied -- according to section 14.9.2.1 -- if the expression foo(x) is well-formed where x is an rvalue of type T. So, the only thing this function requirement guarantees is that the function foo can be invoked with an rvalue argument of type T. Note: There is no word on lvalues. Invoking foo with an lvalue argument of type T is not part of this concept and thus can't be used in restricted templates unless there is also some other requirement specifically allowing this. Again, the proposed resolution by Abrahams and Gregor would unify the meaning of && here.

Finally let's look at how member function requirements can be satisfied. Remember that a member function that is declared without a ref qualifier can be invoked on rvalues and lvalues. What do you think the following concept means?

  concept HasMemberFoo<typename T> {
    void T::foo();
  }

  template<HasMemberFoo T>
    requires DefaultConstructible<T>
  void test(T& lvalue_ref) {
    lvalue_ref.foo(); // OK?
    T().foo(); // OK?
  }

The question is: Will this compile? To answer this we need to check again section 14.9.2.1 of N2800. It says that the expression 'E' that needs to be well-formed in order to satisfy this member function requirement is x.foo() where x is an lvalue of type T. So, the concept doesn't say anything about the function foo being callable on rvalues despite its requirement's appearence. Actually, there is no difference between this concept and the following one:

  concept HasMemberFooRef<typename T> {
    void T::foo() &; // <-- added ref qualifier
  }

This is because the implied expression 'E' that needs to be well-formed is exatly the same. Only in the case of && as a ref qualifier instead the x in the expression is treated as an rvalue of type T -- which in turn doesn't say anything about lvalues. But this is actually no problem because it is what we expect from the && qualifier for member functions. So, the latter case is consistent.

In case you're still with me you will probably agree with me that the satisfaction rule for a member function requirement that lacks a ref qualifier is counter-intuitive. Such a requirement looks like a promise for being able to call the function on rvalues as well as lvalues just like it is the case in good old C++98. This is why I'd like to propose changing this satisfaction rule a bit. In terms of consistency and intuitivity it seems best to require that such a member function requirement is satisfied only when it can be invoked on lvalues and rvalues. Instead of requiring only one expression "E" to be well-formed we would require two expressions "E1" and "E2" to be well-formed that only differ in x being an lvalue in E1 and an rvalue in E2. This change of the rule would not render the C++ language less expressive as far as I can tell. But it would certainly increase consistency with respect to the behaviour of ref qualifiers (or lack thereof) for member functions when both contexts (declaration versus requirement) are compared.

If you want to comment on my proposal to change the rules in 14.9.2.1 please consider writing a response for this usenet thread.

- P

About these ads

Written by pizer

January 8, 2009 at 7:04 pm

4 Responses

Subscribe to comments with RSS.

  1. genius…. great mathematical logic…;)
    u have explained me clearly…thnx so much..

    it seems that u love programming…
    i love too, but i only know java programming.. and it is still a little… I’m a student of Information System in Institute of Technology Sepuluh Nopember, Indonesia… And now still developing my skill…

    nice to talk to you… ;)

    Muhamad Rizal Avif Khan

    January 13, 2009 at 4:54 am

  2. Hey! Thanks! :) But let’s keep it on-topic, please.

    This concept feature is still a bit of a mystery to me.
    Doug Gregor replied to the Usenet thread confirming that I identified a problem.

    pizer

    January 13, 2009 at 4:55 pm

  3. hi Pizer, happy new year!

    I got your point and its fairly interesting (honestly I dont find the reference qualifier for member functions useful, but well)

    Im not sure if this works (and you probably know since it appears you have been reading the standard throughfully) but if not, in my opinion its also worth fixing:

    auto concept cs<typename T> {
    T::T( );
    void T::f(int);
    }

    struct s {
    void f(int start, int length = -1);
    };

    template<cs T>
    void g( )
    {
    T( ).f(1);
    }

    g<s>( ); // does s satisfy cs concept?

    Dark

    January 21, 2009 at 5:06 pm

  4. Hello Dark,

    I took the liberty to edit your comment and add the missing <> brackets WordPress likes to swallow.

    I’m sure your struct ‘s’ satisfies the concept requirements in ‘cs’. But I’m only familiar with the concept checking part of the specification and not so much the part about restricted templates. But I would guess that the restricted template is also well-formed.

    The ref qualifier issue actually extends to normal parameters as well as far as I can tell. There seems to be a hole in the concept system in the way it is verified whether a type fulfills the concept’s requirements or not. I mentioned it in the comp.std.c++ Usenet thread. So far, I havn’t got any responses on that.

    pizer

    January 21, 2009 at 7:29 pm


Leave a Reply

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out / Change )

Twitter picture

You are commenting using your Twitter account. Log Out / Change )

Facebook photo

You are commenting using your Facebook account. Log Out / Change )

Google+ photo

You are commenting using your Google+ account. Log Out / Change )

Connecting to %s

Follow

Get every new post delivered to your Inbox.