Commit 682dfa71 authored by Philipp Unterbrunner's avatar Philipp Unterbrunner Committed by Facebook Github Bot

folly::rvalue_reference_wrapper for store&forward of rvalue references

Summary:
Class template that wraps a reference to an rvalue. Similar to std::reference_wrapper but with three important differences:
1) folly::rvalue_reference_wrappers can only be moved, not copied;
2) the get() function and the conversion-to-T operator are destructive and not const, they invalidate the wrapper;
3) the constructor-from-T is explicit.
These restrictions are designed to make it harder to accidentally create a a dangling rvalue reference, or to use an rvalue reference multiple times. (Using an rvalue reference typically implies invalidation of the target object, such as move-assignment to another object.)

Reviewed By: yfeldblum

Differential Revision: D4931483

fbshipit-source-id: 68453553bf4656ec41976699669a4491fcab79c9
parent 95209f9d
/*
* Copyright 2017-present Facebook, Inc.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
#pragma once
#include <cassert>
#include <memory>
#include <utility>
namespace folly {
/**
* Class template that wraps a reference to an rvalue. Similar to
* std::reference_wrapper but with four important differences:
*
* 1) folly::rvalue_reference_wrappers can only be moved, not copied;
* 2) the get() function and the conversion-to-T operator are destructive and
* not const, they invalidate the wrapper;
* 3) the constructor-from-T is explicit.
*
* These restrictions are designed to make it harder to accidentally create a
* a dangling rvalue reference, or to use an rvalue reference multiple times.
* (Using an rvalue reference typically implies invalidation of the target
* object, such as move-assignment to another object.)
*
* @seealso folly::rref
*/
template <class T>
class rvalue_reference_wrapper {
public:
using type = T;
/**
* Default constructor. Creates an invalid reference. Must be move-assigned
* to in order to be come valid.
*/
rvalue_reference_wrapper() noexcept : ptr_(nullptr) {}
/**
* Explicit constructor to make it harder to accidentally create a dangling
* reference to a temporary.
*/
explicit rvalue_reference_wrapper(T&& ref) noexcept
: ptr_(std::addressof(ref)) {}
/**
* No construction from lvalue reference. Use std::move.
*/
explicit rvalue_reference_wrapper(T&) noexcept = delete;
/**
* Destructive move construction.
*/
rvalue_reference_wrapper(rvalue_reference_wrapper<T>&& other) noexcept
: ptr_(other.ptr_) {
other.ptr_ = nullptr;
}
/**
* Destructive move assignment.
*/
rvalue_reference_wrapper& operator=(
rvalue_reference_wrapper&& other) noexcept {
ptr_ = other.ptr_;
other.ptr_ = nullptr;
return *this;
}
/**
* Implicit conversion to raw reference. Destructive.
*/
/* implicit */ operator T &&() && noexcept {
return static_cast<rvalue_reference_wrapper&&>(*this).get();
}
/**
* Explicit unwrap. Destructive.
*/
T&& get() && noexcept {
assert(valid());
T& ref = *ptr_;
ptr_ = nullptr;
return static_cast<T&&>(ref);
}
/**
* Calls the callable object to whom reference is stored. Only available if
* the wrapped reference points to a callable object. Destructive.
*/
template <class... Args>
decltype(auto) operator()(Args&&... args) &&
noexcept(noexcept(std::declval<T>()(std::forward<Args>(args)...))) {
return static_cast<rvalue_reference_wrapper&&>(*this).get()(
std::forward<Args>(args)...);
}
/**
* Check whether wrapped reference is valid.
*/
bool valid() const noexcept {
return ptr_ != nullptr;
}
private:
// Disallow copy construction and copy assignment, to make it harder to
// accidentally use an rvalue reference multiple times.
rvalue_reference_wrapper(const rvalue_reference_wrapper&) = delete;
rvalue_reference_wrapper& operator=(const rvalue_reference_wrapper&) = delete;
T* ptr_;
};
/**
* Create a folly::rvalue_reference_wrapper. Analogous to std::ref().
*
* Warning: folly::rvalue_reference_wrappers are potentially dangerous, because
* they can easily be used to capture references to temporary values. Users must
* ensure that the target object outlives the reference wrapper.
*
* @example
* class Object {};
* void f(Object&&);
* // BAD
* void g() {
* auto ref = folly::rref(Object{}); // create reference to temporary
* f(std::move(ref)); // pass dangling reference
* }
* // GOOD
* void h() {
* Object o;
* auto ref = folly::rref(std::move(o));
* f(std::move(ref));
* }
*/
template <typename T>
rvalue_reference_wrapper<T> rref(T&& value) noexcept {
return rvalue_reference_wrapper<T>(std::move(value));
}
template <typename T>
rvalue_reference_wrapper<T> rref(T&) noexcept = delete;
}
......@@ -394,6 +394,7 @@ nobase_follyinclude_HEADERS = \
Try.h \
Unicode.h \
Function.h \
Functional.h \
UncaughtExceptions.h \
Unit.h \
Uri.h \
......
/*
* Copyright 2017 Facebook, Inc.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
#include <utility>
#include <folly/Functional.h>
#include <folly/portability/GTest.h>
TEST(RvalueReferenceWrapper, MoveAndConvert) {
using folly::rvalue_reference_wrapper;
// Destructive moves.
int i1 = 0;
rvalue_reference_wrapper<int> rref1(std::move(i1));
ASSERT_TRUE(rref1.valid());
rvalue_reference_wrapper<int> rref0(std::move(rref1));
ASSERT_TRUE(rref0.valid());
ASSERT_FALSE(rref1.valid());
rref1 = std::move(rref0);
ASSERT_FALSE(rref0.valid());
ASSERT_TRUE(rref1.valid());
const int& r1 = std::move(rref1);
ASSERT_FALSE(rref1.valid());
ASSERT_EQ(&r1, &i1);
// Destructive unwrap to T&&.
int i2 = 0;
rvalue_reference_wrapper<int> rref2(std::move(i2));
int&& r2 = std::move(rref2);
ASSERT_EQ(&r2, &i2);
// Destructive unwrap to const T&.
const int i3 = 0;
rvalue_reference_wrapper<const int> rref3(std::move(i3));
const int& r3 = std::move(rref3);
ASSERT_EQ(&r3, &i3);
// Destructive unwrap to const T&&.
const int i4 = 0;
rvalue_reference_wrapper<const int> rref4(std::move(i4));
const int&& r4 = std::move(rref4);
ASSERT_EQ(&r4, &i4);
/*
* Things that intentionally do not compile. Copy construction, copy
* assignment, unwrap of lvalue reference to wrapper, const violations.
*
int i5;
const int i6 = 0;
rvalue_reference_wrapper<int> rref5(i5);
rvalue_reference_wrapper<const int> rref6(i6);
rref1 = rref5;
int& r5 = rref5;
const int& r6 = rref6;
int i7;
const rvalue_reference_wrapper<int> rref7(std::move(i7));
int& r7 = std::move(rref7);
*/
}
TEST(RvalueReferenceWrapper, Call) {
int a = 4711, b, c;
auto callMe = [&](int x, const int& y, int&& z) -> int {
EXPECT_EQ(a, x);
EXPECT_EQ(&b, &y);
EXPECT_EQ(&c, &z);
return a;
};
int result = folly::rref(std::move(callMe))(a, b, std::move(c));
EXPECT_EQ(a, result);
}
......@@ -302,6 +302,11 @@ function_test_SOURCES = \
function_test_LDADD = libfollytestmain.la
TESTS += function_test
functional_test_SOURCES = \
FunctionalTest.cpp
functional_test_LDADD = libfollytestmain.la
TESTS += functional_test
ssl_test_SOURCES = \
../ssl/test/OpenSSLHashTest.cpp
ssl_test_LDADD = libfollytestmain.la -lcrypto
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment