Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Forwarding the const-ness of a template parameter, should I use a forwarding reference?

I want to write a function foo that should call operator() of its parameter, as illustrated in the (broken) code below:

template <typename T> void foo(const T& x){
    x();
}

struct MyFunctor{
    int data;
    void operator()(){
        /* stuff that might modify the data */
    }
};

int main()
{
    foo(MyFunctor{});
}

Obviously the code doesn't work, because operator() is non-const, but foo() requires its parameter to be const.

As a template function, foo() should work with both const and non-const functors, and not be picky about the const-ness of its argument.

If I change foo() by removing the const to the following:

template <typename T> void foo(T& x) { /* ... */ }

... it also won't work because you can't convert an rvalue reference to a non-const lvalue reference, so foo(MyFunctor{}) cannot be called.

Changing foo() to a forwarding reference resolves all the problems:

template <typename T> void foo(T&& x) { /* ... */ }

But is this the "right" way? Shouldn't forwarding references be used only with std::forward() (i.e. the parameter shouldn't be touched apart from forwarding it to another function)?

like image 805
Bernard Avatar asked May 03 '17 09:05

Bernard


1 Answers

Yes, a forwarding reference is the right way, and if it calms you, you can certainly forward the parameter:

template <typename T> void foo(T&& x){
    std::forward<T>(x)();
}

Now it even works with ref-qualified call operators.

like image 165
Kerrek SB Avatar answered Nov 05 '22 00:11

Kerrek SB