Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

disallow passing of rvalue reference to a function

Tags:

c++

c++11

stl

We have the following convenience function that fetches a value from a map or returns a fallback default value if key not found.

template <class Collection> const typename Collection::value_type::second_type&
    FindWithDefault(const Collection& collection,
                    const typename Collection::value_type::first_type& key,
                    const typename Collection::value_type::second_type& value) {
      typename Collection::const_iterator it = collection.find(key);
      if (it == collection.end()) {
        return value;
      }
      return it->second;
    }

The problem with this function is that it allows passing a temporary object as third argument which would be a bug. For example:

const string& foo = FindWithDefault(my_map, "");

Is it possible to disallow passing rvalue references to the third argument in some way by using std::is_rvalue_reference and static assert?

like image 880
Roman Avatar asked Feb 26 '15 10:02

Roman


People also ask

Can you pass an rvalue by reference?

You can pass an object to a function that takes an rvalue reference unless the object is marked as const . The following example shows the function f , which is overloaded to take an lvalue reference and an rvalue reference. The main function calls f with both lvalues and an rvalue.

Can lvalue bind to rvalue reference?

An lvalue reference can bind to an lvalue, but not to an rvalue.

Can a function return an rvalue?

Typically rvalues are temporary objects that exist on the stack as the result of a function call or other operation. Returning a value from a function will turn that value into an rvalue. Once you call return on an object, the name of the object does not exist anymore (it goes out of scope), so it becomes an rvalue.

What is && type C++?

and, && (C++) The and operator is an alternative representation of the && operator (binary or logical AND). If both operands have a value of true, the result has the value true. Otherwise, the result has the value false. Both operands are implicitly converted to bool and the result type is bool.


1 Answers

Adding this additional overload should work (untested):

template <class Collection>
const typename Collection::value_type::second_type&
FindWithDefault(const Collection& collection,
                const typename Collection::value_type::first_type& key,
                const typename Collection::value_type::second_type&& value) = delete;

Overload resolution will select this overload for rvalue references and the = delete makes it a compile time error. Alternatively, if you want to specify a custom message, you could go for

template <class Collection>
const typename Collection::value_type::second_type&
FindWithDefault(const Collection& collection,
                const typename Collection::value_type::first_type& key,
                const typename Collection::value_type::second_type&& value) {
    static_assert(
        !std::is_same<Collection, Collection>::value, // always false
        "No rvalue references allowed!");
}

The std::is_same is there to make the static_assert dependent on the template parameter, otherwise it would cause a compilation error even when the overload is not called.

EDIT: Here is a minimal complete example:

void foo(char const&) { };
void foo(char const&&) = delete;

int main()
{
    char c = 'c';
    foo(c);   // OK
    foo('x'); // Compiler error
}

MSVC gives the following error here for the second call to foo:

rval.cpp(8) : error C2280: 'void foo(const char &&)' : attempting to reference a deleted function
        rval.cpp(2): See declaration of 'foo'

The first call, however, works fine and if you comment out the second then the program compiles.

like image 107
Oberon Avatar answered Oct 28 '22 17:10

Oberon