Topic: member function qualifiers not parameterizable


Author: SG <s.gesemann@gmail.com>
Date: Wed, 29 Jul 2009 12:55:49 CST
Raw View
With ref qualifiers for member functions we now have 12 different
combinations of qualifications:

    4 cv qualifier combinations
  * 3 ref qualifiers (nothing, &, &&)
  ---
   12

Since std::bind is defined in terms of INVOKE (20.7.2) and the
implicit "this" parameter for member functions is not parameterizable
by template parameters, it seems that the std::bind implementation
needs to handle these 12 cases separately.

Has this been considered?
Is it okay to let implementers worry about 12 special cases?
Is there any interest in proposals for fixing it?

Here's just an idea: we could allow a typename to replace cv/ref
qualifiers:

  template < typename Return,
             class Class,
             typename This, // <--- !
             typename... Params >
  memfun_ref_t<Return,Class,This,Params...>
  mem_fun_ref(Return (Class::*pmf)(Params...) This)
  //                                          ^^^^
  {
     return memfun_ref_t<Return,Class,This,Params...>(pmf);
  }

  class A
  {
  public:
    void foo1() ;
    void foo2() & ;
    void foo3() && ;
    void foo4() const ;
    void foo5() const& ;
  };

  void bar()
  {
    auto f1 = mem_fun_ref(&A::foo1); // This = A
    auto f2 = mem_fun_ref(&A::foo2); // This = A &
    auto f3 = mem_fun_ref(&A::foo3); // This = A &&
    auto f4 = mem_fun_ref(&A::foo4); // This = A const
    auto f5 = mem_fun_ref(&A::foo5); // This = A const &
  }

The memfun_ref_t template should be fairly easy to write. Here's a
sketch:

  template<typename Return, class Class,
           typename This, typename... Params>
  class memfun_ref_t
  {
  private:
    Return (Class::*pmf)(Params...) This;

  public:
    explicit memfun_ref_base(
        Return (Class::*pmf)(Params...) This )
    : pmf(pmf) {}

    Return operator()(This && t, Params... p) const
    {
      using std::forward;
      return (forward<This>(t).*pmf)(forward<Params>(p)...);
    }

    requires !LvalueReference<This>
          && !RvalueReference<This>
    Return operator()(This & t, Params... p) const
    {
      late_check {
        using std::forward;
        return (t.*pmf)(forward<Params>(p)...);
      }
    }
  };

I know that concepts are not part of the draft anymore. But I could
not think of another quick solution to support the case of member
functions without ref qualifiers. Since those functions can be invoked
with both, lvalues and rvalues and the first operator() takes care of
rvalues only I used a 2nd operator() function that takes an lvalue
reference.

Opinions?

Cheers!
SG

--
[ comp.std.c++ is moderated.  To submit articles, try just posting with ]
[ your news-reader.  If that fails, use mailto:std-c++@netlab.cs.rpi.edu]
[              --- Please see the FAQ before posting. ---               ]
[ FAQ: http://www.comeaucomputing.com/csc/faq.html                      ]





Author: litb <Schaub-Johannes@web.de>
Date: Wed, 29 Jul 2009 18:08:12 CST
Raw View
On 29 Jul., 20:55, SG <s.gesem...@gmail.com> wrote:
> With ref qualifiers for member functions we now have 12 different
> combinations of qualifications:
>
> [...]
> Here's just an idea: we could allow a typename to replace cv/ref
> qualifiers:
>
>   template < typename Return,
>              class Class,
>              typename This, // <--- !
>              typename... Params >
>   memfun_ref_t<Return,Class,This,Params...>
>   mem_fun_ref(Return (Class::*pmf)(Params...) This)
>   //                                          ^^^^
>   {
>      return memfun_ref_t<Return,Class,This,Params...>(pmf);
>   }
>
>   class A
>   {
>   public:
>     void foo1() ;
>     void foo2() & ;
>     void foo3() && ;
>     void foo4() const ;
>     void foo5() const& ;
>   };
>
>   void bar()
>   {
>     auto f1 = mem_fun_ref(&A::foo1); // This = A
>     auto f2 = mem_fun_ref(&A::foo2); // This = A &
>     auto f3 = mem_fun_ref(&A::foo3); // This = A &&
>     auto f4 = mem_fun_ref(&A::foo4); // This = A const
>     auto f5 = mem_fun_ref(&A::foo5); // This = A const &
>   }
>
> The memfun_ref_t template should be fairly easy to write. Here's a
> sketch:
>
>   template<typename Return, class Class,
>            typename This, typename... Params>
>   class memfun_ref_t
>   {
>   private:
>     Return (Class::*pmf)(Params...) This;
>
>   public:
>     explicit memfun_ref_base(
>         Return (Class::*pmf)(Params...) This )
>     : pmf(pmf) {}
>
>     Return operator()(This && t, Params... p) const
>     {
>       using std::forward;
>       return (forward<This>(t).*pmf)(forward<Params>(p)...);
>     }
>
>     requires !LvalueReference<This>
>           && !RvalueReference<This>
>     Return operator()(This & t, Params... p) const
>     {
>       late_check {
>         using std::forward;
>         return (t.*pmf)(forward<Params>(p)...);
>       }
>     }
>   };
>
> I know that concepts are not part of the draft anymore. But I could
> not think of another quick solution to support the case of member
> functions without ref qualifiers. Since those functions can be invoked
> with both, lvalues and rvalues and the first operator() takes care of
> rvalues only I used a 2nd operator() function that takes an lvalue
> reference.
>
> Opinions?
>

I absolutely support this idea. I had a similar idea, which would
match up with this way nicely. The very similar problem is that
optimally, you often want to return an rvalue reference if the object
expression a member function was called on was an rvalue (like, a
".data()" function). You could enhance your thing, so a template
parameter can be put at the cv/ref qualifies, deducing the part it
appears at. This is how it could look when done manually

     struct vector {
       Data d;
       Data &data() { return d; }
       Data const& data() const& { return d; }
       Data &&data() && { return move(d); }
     };

We could make "T" deduce to "vector" if called on a rvalue object
expression, and to "T&" if called on an lvalue object expression, and
with cv qualifiers where appropriate, like with your member pointer
code.

     struct vector {
       Data d;
       template<typename This>
       typename copy_cv<Data, This>::type &&data() This {
         return static_cast<typename copy_cv<Data, This>::type&&>(d);
       }
     };

I don't like the static cast in the body, though. Maybe a rule could
be added that a return statement in a member function with a && ref-
qualifier regards the expression as an rvalue. We would need to deduce
"T" to "vector&&" for rvalues then. But this sounds weird and isn't in
line with what decltype yields.


--
[ comp.std.c++ is moderated.  To submit articles, try just posting with ]
[ your news-reader.  If that fails, use mailto:std-c++@netlab.cs.rpi.edu]
[              --- Please see the FAQ before posting. ---               ]
[ FAQ: http://www.comeaucomputing.com/csc/faq.html                      ]





Author: SG <s.gesemann@gmail.com>
Date: Thu, 30 Jul 2009 11:28:28 CST
Raw View
On 30 Jul., 02:08, litb <Schaub-Johan...@web.de> wrote:
>
> I absolutely support this idea. I had a similar idea, which would
> match up with this way nicely. The very similar problem is that
> optimally, you often want to return an rvalue reference if the object
> expression a member function was called on was an rvalue (like, a
> ".data()" function). You could enhance your thing, so a template
> parameter can be put at the cv/ref qualifies, deducing the part it
> appears at. This is how it could look when done manually
>
>      struct vector {
>        Data d;
>        Data &data() { return d; }
>        Data const& data() const& { return d; }
>        Data &&data() && { return move(d); }
>      };
>
> We could make "T" deduce to "vector" if called on a rvalue object
> expression, and to "T&" if called on an lvalue object expression, and
> with cv qualifiers where appropriate, like with your member pointer
> code.
>
>      struct vector {
>        Data d;
>        template<typename This>
>        typename copy_cv<Data, This>::type &&data() This {
>          return static_cast<typename copy_cv<Data, This>::type&&>(d);
>        }
>      };
>
> I don't like the static cast in the body, though.

One could use an _extended_ std::forward to do that.

   template<typename T, typename U>
   // TODO: insert SFINAE to disallow
   // forwarding rvalues as lvalues
   inline typename replace_cvrq<U,T>::type&& forward(U && u)
   {
     return static_cast<typename replace_cvrq<U,T>::type&&>(u);
   }

where replace_cvrq<U,T>::type combines the nonqualified type U with
the qualifications of T. This should be backwards compatible and
allows your member function to be written as:

   template<typename This>
   auto data() This -> decltype(std::forward<This>(d))
   {
     return std::forward<This>(d);
   }

I think it would be okay and consistent to deduce "This" in accordance
to the deduction rules for && parameters -- meaning:
 This = vector         for non-const rvalues,
 This = vector&        for non-const lvalues,
 This = vector const&  for const lvalues
:

Cheers!
SG


--
[ comp.std.c++ is moderated.  To submit articles, try just posting with ]
[ your news-reader.  If that fails, use mailto:std-c++@netlab.cs.rpi.edu]
[              --- Please see the FAQ before posting. ---               ]
[ FAQ: http://www.comeaucomputing.com/csc/faq.html                      ]