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?
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.
An lvalue reference can bind to an lvalue, but not to 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.
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.
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.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With