Subject: Re: [boost] [type_traits][function_types] Discard param const qualification, bug or feature?
From: Andrey Semashev (andrey.semashev_at_[hidden])
Date: 2013-09-26 06:02:12


On Thu, Sep 26, 2013 at 1:28 PM, Rob Stewart <robertstewart_at_[hidden]>wrote:

> On Sep 25, 2013, at 5:09 AM, Andrey Semashev <andrey.semashev_at_[hidden]>
> wrote:
>
> > I have to say that while these rules are logical and understandable when
> > explained, types of function arguments are a constant source of
> confusion.
>
> There are many things about C++ that are contrary to someone's intuition.
> That parameter declaration is no different than the following variable:
>
> int const i;
>
> When used with either of the forms of f(), above, does i work any
> differently than j, below?
>
> int j;
>
> They work alike with either f(), because the int is copied. The difference
> is whether you can change the int after initialization. The parameters are
> no different.
>

Sure, they work the same, from the caller's perspective. But is that a
reason to cheat with the type system?

void foo(int);
void bar(const int);

typeid(&foo) == typeid(&bar); // why?

My point was that despite the same behavior on the caller's side, the
functions have different signatures, and I don't see why there was a
_necessity_ to force the compiler to drop cv-qualifiers from the function
argument types. In other words, it makes the language more complicated for
no apparent reason.

> > But C++11 brought us
> > rvalue references, and the following:
> >
> > foo(int&& n)
> > {
> > // n is _not_ rvalue reference here
> > }
> >
> > I understand the rationale for this, and it seems the right thing. But
> once in a while, when yet another fellow developer asks me why is that so,
> I wonder if it would be better if the standard was more straight-forward in
> > this part.
>
> The issue comes down to one of consequences. If n were still an rvalue,
> within foo(), even when referenced by name, what problems will that cause?
>

AFAIR, the motivating example was something like this:

  void bar(int&& n); // moves from n

  void foo(int&& n)
  {
    bar(n);
    bar(n); // moves from a moved-from object
    ++n; // uses a moved-from object
  }

This can be a real gotcha, I admit. But just as well as this:

  class my_class
  {
    vector<int> vec;

  public:
    my_class(vector<int>&& v) : vec(v) // copies the vector, instead of
moving
    {
    }
  };

This latter mistake is usually less critical, but for that reason it is
also more often made and left unnoticed.