greywolf82
greywolf82

Reputation: 22173

C++11 overloaded methods with forwarding to a unique method

I have the following class in c++11:

#include <iostream>
#include <string>
#include <utility>

void overloaded_function(const std::string& param) {
  someStuffHere
}
void overloaded_function(std::string&& param) {
  someStuffHere
}

As you can see the overloaded_function implementation is exactly the same. I would like to create a new function instead of having the same implementation into two different methods. I tried with:

void overloaded_function(const std::string& param) {
  uniqueCall(std::forward<std::string&>(param));
}
void overloaded_function(std::string&& param) {
  uniqueCall(std::forward<std::string&&>(param));
}
void uniqueCall(T&& param) {
    someStuffHere
}

but it doesn't work due to the lvalue reference not accepted by uniqueCall.

Upvotes: 8

Views: 615

Answers (4)

Deduplicator
Deduplicator

Reputation: 45654

I suggest delegating directly to a template function:

void overloaded_function(const std::string& param) {
  uniqueCall(param);
}
void overloaded_function(std::string&& param) {
  uniqueCall(std::move(param));
}
template <class T>
void uniqueCall(T&& param) {
    someStuffHere
}

If you identify some substantial enough amount off work not benefitting from treating param as anything but a const std::string&, it might make sense for executable-size or organization to promote it to its own function. Maybe it makes sense to treat it as a C++17 std::string_view for that?

That template-function you delegate to, or the helper-function you call from it, might even be some interesting and valuable addition to your interface, maybe after a minor adjustment, see .emplace_@() and .put_@() in the standard-library containers.

Upvotes: 1

PiotrNycz
PiotrNycz

Reputation: 24402

Once r-value parameter gets a name - it can be treated, this named parameter, as l-value reference, as you can read in http://en.cppreference.com/w/cpp/language/value_category:

Even if the variable's type is rvalue reference, the expression consisting of its name is an lvalue expression;

So you might write as simple as this:

void overloaded_function(std::string& param) {
  someStuffHere
}
void overloaded_function(std::string&& param) {
  overloaded_function(param); // here param type is std::string&
}

If you really enjoy to have "common" function - it shall accept lvalue ref for the very same reasons:

void uniqueCall(std::string& param) {
  some stuff here
}
void overloaded_function(std::string& param) {
  uniqueCall(param)
}
void overloaded_function(std::string&& param) {
  uniqueCall(param); // here param type is std::string&
}

Upvotes: 5

Richard Hodges
Richard Hodges

Reputation: 69882

A common pattern is to have the argument type of the interface function deduced:

template<class Param>
void overloaded_function(Param&& param)
{
    commonStuff(param);
    mutabilityDependentStuff(std::forward<Param>(param));    
}

Regardless of whether Param is an r-value or l-value reference, it can decay into a const l-value reference:

void commonStuff(std::string const& arg)
{
    //
}

And std::forward<> can use used to detect proper decay into mutable and immutable versions (mutable r-values will decay to mutable l-values):

void mutabilityDependentStuff(std::string const& arg)
{
    //
}

void mutabilityDependentStuff(std::string& arg)
{
    //
}

Upvotes: 1

If you don't need to modify the argument, you don't need an rvalue overload at all1. A const lvalue reference will happily bind to an rvalue.

That's how C++ always worked, and rvalue references don't change that.

So a simple

void overloaded_function(const std::string& param) {
  //someStuffHere
}

Will bind to an argument of whatever value category.


1 - And if you did need to modify it, then passing rvalues is a bit dubious.

Upvotes: 7

Related Questions