C++ Logo

std-proposals

Advanced search

[std-proposals] The syntax of forward and move macros

From: Amar Saric <asaric_at_[hidden]>
Date: Mon, 29 Aug 2022 01:31:19 +0200
Perfect forwarding received a lot of attention initially when it was first
added. By now, everybody should be clear on how it is used and what its
purpose is. However, the syntax leaves something to be desired, as the
forward template requires a parameter, which is actually not necessary, and
frankly a bit ugly. This can be completely avoided by stripping the
references and using a macro, as follows:



#include <iostream>

#include <string>

#include <utility>





namespace pf

{

template <typename T> struct check_if_ref

{

    static_assert(std::is_reference<T>::value, "ref_forward needs a
reference");

    typedef T type;

};

template <typename T> struct _remove_rvalue_reference

{

    typedef T type;

};

template <typename T> struct _remove_rvalue_reference<T &&>

{

    typedef T type;

};

template <typename T>

using remove_rvalue_reference = typename _remove_rvalue_reference<T>::type;

}

#define ref_forward(p) (std::forward<pf::\

        remove_rvalue_reference<typename pf::\

        check_if_ref<decltype(p)>::type>>(p))

#define ref_to_rvalue(p) (std::move(p))





using std::cout;

using std::endl;

using std::string;



class Name

{

public:

    Name(string& aName) : name(aName)

    {

        cout << "Lvalue Name constructor." << endl;

    }



    Name(string&& aName) : name(ref_to_rvalue(aName))

    {

        cout << "Rvalue Name constructor." << endl;

    }



    const string& getName() const { return name; }



private:

    string name;

};



class Dog

{

public:

    template <typename T> Dog(T&& name) : name(ref_forward(name)) {}

    string getName() const { return name.getName(); }



private:

    Name name;

};



void print(Dog& dog) { cout << "Dog is " << dog.getName() << endl; }



int main()

{

    cout << "Dog(string(\"Fido\"))) - rvalue argument:" << endl;

    Dog dog(string("Fido"));

    print(dog);



    cout << "Dog(\"Lassie\")) - rvalue const char* argument:" << endl;

    Dog another("Lassie");

    print(another);



    string var_name("Woofie");

    cout << "Dog(var_name) - lvalue argument:" << endl;

    Dog yetanother(var_name);

    print(yetanother);



    return 0;

}



Output:



Dog(string("Fido"))) - rvalue argument:

Rvalue Name constructor.

Dog is Fido

Dog("Lassie")) - rvalue const char* argument:

Rvalue Name constructor.

Dog is Lassie

Dog(var_name) - lvalue argument:

Lvalue Name constructor.

Dog is Woofie



Providing two macros similar to the ones above in a separate header would
be nice to have in my opinion and, considering how may other features have
been added over time, maybe others will feel the same. One could argue that
this is just a matter of taste, but macros are used in other places as
well. It is in a nutshell what I originally expected it to look like back
in the day – what I personally find intuitive – and no means not cast in
stone.



Tell me what you think: Just a hack or is it worth it?



Best,



Amar

Received on 2022-08-28 23:31:32