I'm trying to support tuple-like structured binding access for a class. For simplicity, I'll use the following class in the rest of this post:
struct Test
{
int v = 42;
};
(I'm aware that this class supports structured bindings out of the box but let's assume it does not.)
To enable tuple-like access to the member of Test, we must specialize std::tuple_size and std::tuple_element:
namespace std
{
template<>
struct tuple_size<Test>
{
static const std::size_t value = 1;
};
template<std::size_t I>
struct tuple_element<I, Test>
{
using type = int;
};
}
And the last part we need is either Test::get<i> or a function get<i>(Test) in Test's namespace. Let's implement the latter:
template<std::size_t I>
int get(Test t)
{
return t.v;
}
This works. However, I would like to return a reference to Test's member, just like std::get(std::tuple), for example. Therefore, I implement get as follows:
template<std::size_t I>
int& get(Test& t)
{
return t.v;
}
template<std::size_t I>
const int& get(const Test& t)
{
return t.v;
}
With this version, however, the following code
auto test = Test{};
auto [v] = test;
produces an error (GCC 7.1):
binding reference of type ‘std::tuple_element<0, Test>::type& {aka int&}’ to ‘const int’ discards qualifiers
So it seems as if the get<i>(const Test&) overload is selected for the structured binding. Since this overload returns a const int&, and v acts like a non-const reference to int, the code fails to compile.
According to this, however, the line auto [v] = test; should be roughly equivalent to
auto e = test;
std::tuple_element<0, Test>::type& v = get<0>(e)
Which does work since it uses the get<i>(Test&) overload.
Any ideas on why my implementation of get does not work for structured bindings?
The problem is that auto [v] is a non-reference declaration, so test is copied and the copy of test is passed to get as an xvalue.
So you need to add an rvalue qualified get:
template<std::size_t I>
int&& get(Test&& t)
{
return std::move(t.v);
}
Related
I want to store passed data via constexpr constructor of a struct, and store the data in a std::tuple, to perform various TMP / compile time operations.
Implementation
template <typename... _Ts>
struct myInitializer {
std::tuple<_Ts...> init_data;
constexpr myInitializer(_Ts&&... _Vs)
: init_data{ std::tuple(std::forward<_Ts>(_Vs)...) }
{}
};
Stored data uses a lightweight strong type struct, generated via lvalue and rvalue helper overload:
template <typename T, typename... Ts>
struct data_of_t {
using type = T;
using data_t = std::tuple<Ts...>;
data_t data;
constexpr data_of_t(Ts&&... _vs)
: data(std::forward<Ts>(_vs)...)
{}
};
template<typename T, typename... Ts>
constexpr auto data_of(Ts&&... _vs) {
return data_of_t<T, Ts...>(std::forward<Ts>(_vs)...);
};
template<typename T, typename... Ts>
constexpr auto data_of(Ts&... _vs) {
return data_of_t<T, Ts...>(std::forward<Ts>(_vs)...);
};
It's implemented like
template <typename T = int>
class test {
public:
static constexpr auto func(int p0=0, int p1=1, int p2=3) noexcept {
return data_of <test<T>>
(data_of<test<T>>(p0, p1));
}
};
int main() {
constexpr // fails to run constexpr // works without
auto init = myInitializer (
test<int>::func()
,test<int>::func(3)
,test<int>::func(4,5)
);
std::apply([&](auto&&... args) {
//std::cout << __PRETTY_FUNCTION__ << std::endl;
auto merged_tuple = std::tuple_cat(std::forward<decltype(args.data)>(args.data)...);
}
, init.init_data);
}
Getting to the point
std::tuple_cat fails if myInitializer instance is constexpr.
std::apply([&](auto&&... args) {
auto merged_tuple = std::tuple_cat(std::forward<decltype(args.data)>(args.data)...);
It appears to be related to the const qualifier added via constexpr.
How can this be fixed?
See full example at https://godbolt.org/z/j5xdT39aE
This:
auto merged_tuple = std::tuple_cat(std::forward<decltype(args.data)>(args.data)...);
is not the right way to forward data. decltype(args.data) is going to give you the type of that data member - which is not a function of either the const-ness or value category of args. Let's take a simpler example:
void f(auto&& arg) {
g(std::forward<decltype(arg.data)>(arg.data));
}
struct C { int data; };
C c1{1};
const C c2{2};
f(c1);
f(c2);
f(C{3});
So here I have three calls to f (which call f<C&>, f<const C&>, and f<C>, respectively). In all three cases, decltype(arg.data) is... just int. That's what the type of C::data is. But that's not how it needs to be forwarded (it won't compile for c2 because we're trying to cast away const-ness -- as in your example -- and it'll erroneously move out of c1).
What you want is to forward arg, separately, and then access data:
void f(auto&& arg) {
g(std::forward<decltype(arg)>(arg).data);
}
Now, decltype(arg) actually varies from instantiation to instantiation, which is a good indicator that we're doing something sensible.
In addition of the forwarding problem denoted by Barry, there's a different reason why you cannot have constexpr on init. This is because you contain a reference to a temporary inside data_of_t.
You see, you are containing a type obtained from overload resolution from a forwarding reference:
template<typename T, typename... Ts>
constexpr auto data_of(Ts&&... _vs) {
return data_of_t<T, Ts...>(std::forward<Ts>(_vs)...);
};
The Ts... in this case could be something like int, float const&, double&. You send those reference type and then you contain them inside of the std::tuple in data_of_t.
Those temporaries are local variables from the test function:
template <typename T = int>
class test {
public:
static constexpr auto func(int p0=0, int p1=1, int p2=3) noexcept {
return data_of <test<T>>
(data_of<test<T>>(p0, p1));
}
};
The problem here is that p0, p1, p2 are all local variable. You send them in test_of_t which will contain references to them, and you return the object containing all those reference to the local variable. This is maybe the cause of the MSVC crash. Compiler are required to provide diagnostic for any undefined behaviour in constexpr context. This crash is 100% a compiler bug and you should report it.
So how do you fix that?
Simply don't contain references by changing data_of:
template<typename T, typename... Ts>
constexpr auto data_of(Ts&&... _vs) {
return data_of_t<T, std::decay_t<Ts>...>(std::forward<Ts>(_vs)...);
};
This will decay the type thus removing the references and decay any reference to C array to pointers.
Then, you have to change your constructor. You call std::forward in there but it's no forwarding occurring if you decay in the template arguments.
template<typename... Vs> requires((std::same_as<std::decay_t<Vs>, Ts>) && ...)
constexpr data_of_t(Vs... _vs)
: data(std::forward<Vs>(_vs)...)
{}
This will add proper forwarding and also constrain it properly so it always do as data_of intended.
Just doing those change will remove UB from the code, but also change it a bit. The type data_of_t will always contain values, and won't contain references. If you want to send a reference, you will need something like std::ref, just like std::bind have to use to defer parameters.
You will still need to use std::forward<decltype(arg)>(arg).data for proper forwarding as #Barry stated
While implementing a compressed_tuple class for some project I'm working on, I ran into the following issue: I can't seem to pass instances of this type to std::apply, even though this should be possible according to: https://en.cppreference.com/w/cpp/utility/apply.
I managed to reproduce the issue quite easily, using the following fragment (godbolt):
#include <tuple>
struct Foo {
public:
explicit Foo(int a) : a{ a } {}
auto &get_a() const { return a; }
auto &get_a() { return a; }
private:
int a;
};
namespace std {
template<>
struct tuple_size<Foo> {
constexpr static auto value = 1;
};
template<>
struct tuple_element<0, Foo> {
using type = int;
};
template<size_t I>
constexpr auto get(Foo &t) -> int & {
return t.get_a();
}
template<size_t I>
constexpr auto get(const Foo &t) -> const int & {
return t.get_a();
}
template<size_t I>
constexpr auto get(Foo &&t) -> int && {
return std::move(t.get_a());
}
template<size_t I>
constexpr auto get(const Foo &&t) -> const int && {
return move(t.get_a());
}
} // namespace std
auto foo = Foo{ 1 };
auto f = [](int) { return 2; };
auto result = std::apply(f, foo);
When I try to compile this piece of code, it seems that it cannot find the std::get overloads that I have defined, even though they should perfectly match. Instead, it tries to match all of the other overloads (std::get(pair<T, U>), std::get(array<...>), etc.), while not even mentioning my overloads. I get consistent errors in all three major compilers (MSVC, Clang, GCC).
So my question is whether this is expected behavior and it's simply not possible to use std::apply with user-defined types? And is there a work-around?
So my question is whether this is expected behavior and it's simply
not possible to use std::apply with user-defined types?
No, there is currently no way.
In libstdc++, libc++, and MSVC-STL implementations, std::apply uses std::get internally instead of unqualified get, since users are prohibited from defining get under namespace std, it is impossible to apply std::apply to user-defined types.
You may ask, in [tuple.creation], the standard describes tuple_cat as follows:
[Note 1: An implementation can support additional types in the
template parameter pack Tuples that support the tuple-like protocol,
such as pair and array. — end note]
Does this indicate that other tuple utility functions such as std::apply should support user-defined tuple-like types?
Note that in particular, the term "tuple-like" has no concrete definition at
this point of time. This was intentionally left C++ committee to make this gap
being filled by a future proposal. There exists a proposal that is
going to start improving this matter, see P2165R3.
And is there a work-around?
Before P2165 is adopted, unfortunately, you may have to implement your own apply and use non-qualified get.
The question has throughly been answered by #康桓瑋. I'm going to post some more details on how to provide a workaround.
First, here is a generic C++20 apply function:
#include<tuple>
#include<functional>
namespace my
{
constexpr decltype(auto) apply(auto&& function, auto&& tuple)
{
return []<size_t ... I>(auto && function, auto && tuple, std::index_sequence<I...>)
{
using std::get;
return std::invoke(std::forward<decltype(function)>(function)
, get<I>(std::forward<decltype(tuple)>(tuple)) ...);
}(std::forward<decltype(function)>(function)
, std::forward<decltype(tuple)>(tuple)
, std::make_index_sequence<std::tuple_size_v<std::remove_reference_t<decltype(tuple)> > >{});
}
} //namespace my
The own namespace is useful so that the custom apply does not interfere with the std-version. The unqualified call to get means (quoting #Quuxplusone from his blog, which gives the best explanation I encountered so far):
An unqualified call using the two-step, like using my::xyzzy;
xyzzy(t), indicates, “I know one way to xyzzy whatever this thing may
be, but T itself might know a better way. If T has an opinion, you
should trust T over me.”
You can then roll out your own tuple-like class,
struct my_tuple
{
std::tuple<int,int> t;
};
template<size_t I>
auto get(my_tuple t)
{
return std::get<I>(t.t);
}
namespace std
{
template<>
struct tuple_size<my_tuple>
{
static constexpr size_t value = 2;
};
}
With the overload of get() and the specialization of std::tuple_size, the apply function then works as expected. Moreover, you can plug in any compliant std-type:
int main()
{
auto test = [](auto ... x) { return 1; };
my::apply(test, my_tuple{});
my::apply(test, std::tuple<int,double>{});
my::apply(test, std::pair<int,double>{});
my::apply(test, std::array<std::string,10>{});
}
DEMO
I wonder how the following can be done
void f(string &&s) {
std::string i(move(s));
/* other stuff */
}
int main() {
std::string s;
bind(f, s)(); // Error.
bind(f, move(s))(); // Error.
bind(f, ref(s))(); // Error.
}
How can I pass an rvalue reference and store it as an rvalue reference (possibly wrapped) in the call wrapper? I know I can manually write up a class like std::reference_wrapper<> that has a conversion function to T&&, but I would rather want to avoid that and use Standard technology.
I implemented it like AProgrammer recommends:
template<typename T> struct adv {
T t;
explicit adv(T &&t):t(forward<T>(t)) {}
template<typename ...U> T &&operator()(U &&...) {
return forward<T>(t);
}
};
template<typename T> adv<T> make_adv(T &&t) {
return adv<T>{forward<T>(t)};
}
namespace std {
template<typename T>
struct is_bind_expression< adv<T> > : std::true_type {};
}
Now I can say
void f(string &&s) {
std::string i(move(s));
/* other stuff */
}
int main() {
std::string s;
bind(f, make_adv(move(s)))(); // Works!
}
If we pass an lvalue to make_adv, it will forward it as an lvalue referring to the input argument, so it can be used as a replacement for std::ref, in this case.
My take on this.
20.8.10.1.2/10 in N3225
The values of the bound arguments v1, v2, ..., vN and their corresponding types V1, V2, ..., VN
depend on the types TiD derived from the call to bind and the cv-qualifiers cv of the call wrapper g as
follows:
if TiD is reference_wrapper, the argument is tid.get() and its type Vi is T&;
if the value of is_bind_expression::value is true, the argument is tid(std::forward(uj)...)
and its type Vi is result_of::type;
if the value j of is_placeholder::value is not zero, the argument is std::forward(uj)
and its type Vi is Uj&&;
otherwise, the value is tid and its type Vi is TiD cv &.
So the only possibility to have a rvalue reference is to have is_bind_expression<TiD>::value true or is_placeholder<TiD>::value not zero. The second possibility has implications you don't want and achieving the wanted result with the first would imply that the problem we are trying to solve is solved if we restrict to the standard provided types. So, the only possibility would be to provide your own wrapper and a specialisation for is_bind_expression<TiD> (that is allowed by 20.8.10.1.1/1) as I don't see one.
How can I pass an rvalue reference and store it as an rvalue reference in the call wrapper?
The problem here is that such a bind function object can be invoked multiple times. If the function object forwarded a bound parameter as rvalue this would obviously only work once. So, this is a bit of a safety issue.
But in some cases this kind of forwarding is exactly what you want. You could use a lambda as an intermediary:
bind([](string& s){f(move(s));},move(s));
Basically, I came up with this bind+lambda combination as a workaround for a missing "move-capture".
I was googling for "reference_wrapper for rvalues" when I stumbled on this question.
Not sure whether my answer would be useful, it is not related to std::bind and actually doesn't work with it, but for some other use cases it might help somebody.
Here's my attempt to implement rvalue_reference_wrapper:
#pragma once
#include <type_traits>
#include <memory>
#include <utility>
template<class T>
class rvalue_reference_wrapper
{
public:
static_assert(::std::is_object<T>::value, "rvalue_reference_wrapper<T> requires T to be an object type.");
using type = T;
rvalue_reference_wrapper(T& ref_value) = delete;
rvalue_reference_wrapper(T&& ref_value) noexcept
: _pointer(::std::addressof(ref_value))
{
}
operator T&&() && noexcept
{
return ::std::move(*_pointer);
}
T&& get() && noexcept
{
return ::std::move(*_pointer);
}
template<class... ArgTypes>
auto operator()(ArgTypes&&... args) &&
-> decltype(::std::invoke(::std::declval<rvalue_reference_wrapper<T>>().get(), ::std::forward<ArgTypes>(args)...))
{
return (::std::invoke(::std::move(*this).get(), ::std::forward<ArgTypes>(args)...));
}
private:
T* _pointer;
};
template<class T>
inline rvalue_reference_wrapper<T> rv_ref(T& ref_value) = delete;
template<class T>
inline ::std::enable_if_t<!(::std::is_lvalue_reference<T>::value), rvalue_reference_wrapper<T>> rv_ref(T&& ref_value) noexcept
{
return rvalue_reference_wrapper<T>(::std::forward<T>(ref_value));
}
#ifdef _MSC_VER
namespace std
{
template<class T>
struct _Unrefwrap_helper<rvalue_reference_wrapper<T>>
{
using type = T &&;
static constexpr bool _Is_refwrap = true;
};
}
#else
#pragma error("TODO : implement...")
#endif
The last specialization in namespace std allows MSVC's implementation of standard library to work with my type, e.g. when using std::make_tuple:
int a = 42;
auto p_int = std::make_unique<int>(42);
auto test_tuple = std::make_tuple(42, std::ref(a), rv_ref(std::move(p_int)));
static_assert(std::is_same<decltype(test_tuple), std::tuple<int, int &, std::unique_ptr<int>&&>>::value, "unexpected result");
I believe it would not be hard to implement similar "unwrapping" logic for other standard library implementations.
You can use a mutable lambda object.
auto func = [=]() mutable {
f(std::move(s));
};
I'd like to hide a std::tuple in my class 'Record' and provide an operator[] on it to access elements of the tuple. The naive code that does not compile is this:
#include <tuple>
template <typename... Fields>
class Record {
private:
std::tuple<Fields...> list;
public:
Record() {}
auto operator[](std::size_t n)
-> decltype(std::get<1u>(list)) {
return std::get<n>(list);
}
};
int main() {
Record<int, double> r;
r[0];
return 0;
}
g++ 4.6 says:
x.cc:13:32: error: no matching function for call to ‘get(std::tuple<int, double>&)’
x.cc:13:32: note: candidates are:
/usr/include/c++/4.6/utility:133:5: note: template<unsigned int _Int, class _Tp1, class _Tp2> typename std::tuple_element<_Int, std::pair<_Tp1, _Tp2> >::type& std::get(std::pair<_Tp1, _Tp2>&)
/usr/include/c++/4.6/utility:138:5: note: template<unsigned int _Int, class _Tp1, class _Tp2> const typename std::tuple_element<_Int, std::pair<_Tp1, _Tp2> >::type& std::get(const std::pair<_Tp1, _Tp2>&)
/usr/include/c++/4.6/tuple:531:5: note: template<unsigned int __i, class ... _Elements> typename std::__add_ref<typename std::tuple_element<__i, std::tuple<_Elements ...> >::type>::type std::get(std::tuple<_Elements ...>&)
/usr/include/c++/4.6/tuple:538:5: note: template<unsigned int __i, class ... _Elements> typename std::__add_c_ref<typename std::tuple_element<__i, std::tuple<_Elements ...> >::type>::type std::get(const std::tuple<_Elements ...>&)
Basically I'd like to call Record::operator[] just like on an array. is this possible?
The argument to get is a compile time constant. You cannot use a
runtime variable for this and you cannot have a single function that
returns the tuple members as your return type is going to be
wrong. What you can do is to abuse non-type argument deduction:
#include <tuple>
template<typename... Args>
struct Foo {
std::tuple<Args...> t;
template<typename T, std::size_t i>
auto operator[](T (&)[i]) -> decltype(std::get<i>(t)) {
return std::get<i>(t);
}
// also a const version
};
int main()
{
Foo<int, double> f;
int b[1];
f[b];
return 0;
}
This is so horrible, that I would never use it and it won't make much sense to users. I would just forward get through a template member.
I'll try to explain why I think why this is really evil: The return type of a function depends only on compile time facts (this changes slightly for virtual member functions). Let's just assume that non-type argument deduction were possible for some cases (the function call arguments are constexpr) or that we could build something that hides it reasonably well, your users wouldn't realize that their return type just changed and implicit conversion would do nasty things to them. Making this explicit safes some of the trouble.
The error message seems to be misleading, as the problem with your code is pretty much clear:
auto operator[](std::size_t n)
-> decltype(std::get<1u>(list)) {
return std::get<n>(list);
}
The template argument n to std::get must be a constant expression, but in your code above n is not a constant expression.
No.
It is not possible to use a parameter bound at runtime (such as a function parameter) to act as template parameter, because such need be bound at compile-time.
But let's imagine for a second that it was:
Record<Apple, Orange> fruitBasket;
Then we would have:
decltype(fruitBasket[0]) equals Apple
decltype(fruitBasket[1]) equals Orange
is there not something here that bothers you ?
In C++, a function signature is defined by the types of its arguments (and optionally the values of its template parameters). The return type is not considered and does not participate (for better or worse) in the overload resolution.
Therefore, the function you are attempting to build simply does not make sense.
Now, you have two alternatives:
require that all arguments inherit or be convertible to a common type, and return that type (which allows you to propose a non-template function)
embrace templates and require your users to provide specifically the index of the type they wish to use
I do not (and cannot) which alternative is preferable in your particular situation, this is a design choice you will have to make.
Finally, I will remark that you may be reasoning at a too low level. Will your users really need to access each field independently ? If they don't, you could provide facilities to apply functions (visitors ?) to each element in turn, for example.
I think Xeo had code which did this.
Here is my attempt which somewhat works. The problem is that [] is not a reference.
template<typename T, std::size_t N = std::tuple_size<T>::value - 1>
struct foo {
static inline auto bar(std::size_t n, const T& list)
-> decltype(((n != N) ? foo<T, N-1>::bar(n, list) : std::get<N>(list))) {
return ((n != N) ? foo<T, N-1>::bar(n, list) : std::get<N>(list));
}
};
template<typename T>
struct foo<T, 0> {
static inline auto bar(std::size_t n, const T& list)
-> decltype(std::get<0>(list)) {
return std::get<0>(list);
}
};
template <typename... Fields>
class Record {
private:
std::tuple<Fields...> list;
public:
Record() {
std::get<0>(list) = 5;
}
inline auto operator[](std::size_t n)
-> decltype(foo<decltype(list)>::bar(n, list)) {
return foo<decltype(list)>::bar(n, list);
}
};
int main() {
Record<int, double> r;
std::cout << r[0];
return 0;
}
As n is a template parameter, it should be known in compile time, but you want to pass it as a parameter in run-time.
Also, gcc 4.5.2 isn't happy due to this fact:
g++ 1.cpp -std=c++0x
1.cpp: In member function 'decltype (get<1u>(((Record<Fields>*)0)->Record<Fields>::list)) Record<Fields>::operator[](size_t)':
1.cpp:14:25: error: 'n' cannot appear in a constant-expression
If you're fine with a compile-time constant and still want to have the nice operator[] syntax, this is an interesting workaround:
#include <tuple>
template<unsigned I>
struct static_index{
static unsigned const value = I;
};
template <typename... Fields>
class Record {
private:
typedef std::tuple<Fields...> tuple_t;
tuple_t list;
public:
Record() {}
template<unsigned I>
auto operator[](static_index<I>)
-> typename std::tuple_element<
I, tuple_t>::type&
{
return std::get<I>(list);
}
};
namespace idx{
const static_index<0> _0 = {};
const static_index<1> _1 = {};
const static_index<2> _2 = {};
const static_index<3> _3 = {};
const static_index<4> _4 = {};
}
int main() {
Record<int, double> r;
r[idx::_0];
return 0;
}
Live example on Ideone. Though I'd personally just advise to do this:
// member template
template<unsigned I>
auto get()
-> typename std::tuple_element<
I, tuple_t>::type&
{
return std::get<I>(list);
}
// free function
template<unsigned I, class... Fields>
auto get(Record<Fields...>& r)
-> decltype(r.template get<I>())
{
return r.template get<I>();
}
Live example on Ideone.
I wonder how the following can be done
void f(string &&s) {
std::string i(move(s));
/* other stuff */
}
int main() {
std::string s;
bind(f, s)(); // Error.
bind(f, move(s))(); // Error.
bind(f, ref(s))(); // Error.
}
How can I pass an rvalue reference and store it as an rvalue reference (possibly wrapped) in the call wrapper? I know I can manually write up a class like std::reference_wrapper<> that has a conversion function to T&&, but I would rather want to avoid that and use Standard technology.
I implemented it like AProgrammer recommends:
template<typename T> struct adv {
T t;
explicit adv(T &&t):t(forward<T>(t)) {}
template<typename ...U> T &&operator()(U &&...) {
return forward<T>(t);
}
};
template<typename T> adv<T> make_adv(T &&t) {
return adv<T>{forward<T>(t)};
}
namespace std {
template<typename T>
struct is_bind_expression< adv<T> > : std::true_type {};
}
Now I can say
void f(string &&s) {
std::string i(move(s));
/* other stuff */
}
int main() {
std::string s;
bind(f, make_adv(move(s)))(); // Works!
}
If we pass an lvalue to make_adv, it will forward it as an lvalue referring to the input argument, so it can be used as a replacement for std::ref, in this case.
My take on this.
20.8.10.1.2/10 in N3225
The values of the bound arguments v1, v2, ..., vN and their corresponding types V1, V2, ..., VN
depend on the types TiD derived from the call to bind and the cv-qualifiers cv of the call wrapper g as
follows:
if TiD is reference_wrapper, the argument is tid.get() and its type Vi is T&;
if the value of is_bind_expression::value is true, the argument is tid(std::forward(uj)...)
and its type Vi is result_of::type;
if the value j of is_placeholder::value is not zero, the argument is std::forward(uj)
and its type Vi is Uj&&;
otherwise, the value is tid and its type Vi is TiD cv &.
So the only possibility to have a rvalue reference is to have is_bind_expression<TiD>::value true or is_placeholder<TiD>::value not zero. The second possibility has implications you don't want and achieving the wanted result with the first would imply that the problem we are trying to solve is solved if we restrict to the standard provided types. So, the only possibility would be to provide your own wrapper and a specialisation for is_bind_expression<TiD> (that is allowed by 20.8.10.1.1/1) as I don't see one.
How can I pass an rvalue reference and store it as an rvalue reference in the call wrapper?
The problem here is that such a bind function object can be invoked multiple times. If the function object forwarded a bound parameter as rvalue this would obviously only work once. So, this is a bit of a safety issue.
But in some cases this kind of forwarding is exactly what you want. You could use a lambda as an intermediary:
bind([](string& s){f(move(s));},move(s));
Basically, I came up with this bind+lambda combination as a workaround for a missing "move-capture".
I was googling for "reference_wrapper for rvalues" when I stumbled on this question.
Not sure whether my answer would be useful, it is not related to std::bind and actually doesn't work with it, but for some other use cases it might help somebody.
Here's my attempt to implement rvalue_reference_wrapper:
#pragma once
#include <type_traits>
#include <memory>
#include <utility>
template<class T>
class rvalue_reference_wrapper
{
public:
static_assert(::std::is_object<T>::value, "rvalue_reference_wrapper<T> requires T to be an object type.");
using type = T;
rvalue_reference_wrapper(T& ref_value) = delete;
rvalue_reference_wrapper(T&& ref_value) noexcept
: _pointer(::std::addressof(ref_value))
{
}
operator T&&() && noexcept
{
return ::std::move(*_pointer);
}
T&& get() && noexcept
{
return ::std::move(*_pointer);
}
template<class... ArgTypes>
auto operator()(ArgTypes&&... args) &&
-> decltype(::std::invoke(::std::declval<rvalue_reference_wrapper<T>>().get(), ::std::forward<ArgTypes>(args)...))
{
return (::std::invoke(::std::move(*this).get(), ::std::forward<ArgTypes>(args)...));
}
private:
T* _pointer;
};
template<class T>
inline rvalue_reference_wrapper<T> rv_ref(T& ref_value) = delete;
template<class T>
inline ::std::enable_if_t<!(::std::is_lvalue_reference<T>::value), rvalue_reference_wrapper<T>> rv_ref(T&& ref_value) noexcept
{
return rvalue_reference_wrapper<T>(::std::forward<T>(ref_value));
}
#ifdef _MSC_VER
namespace std
{
template<class T>
struct _Unrefwrap_helper<rvalue_reference_wrapper<T>>
{
using type = T &&;
static constexpr bool _Is_refwrap = true;
};
}
#else
#pragma error("TODO : implement...")
#endif
The last specialization in namespace std allows MSVC's implementation of standard library to work with my type, e.g. when using std::make_tuple:
int a = 42;
auto p_int = std::make_unique<int>(42);
auto test_tuple = std::make_tuple(42, std::ref(a), rv_ref(std::move(p_int)));
static_assert(std::is_same<decltype(test_tuple), std::tuple<int, int &, std::unique_ptr<int>&&>>::value, "unexpected result");
I believe it would not be hard to implement similar "unwrapping" logic for other standard library implementations.
You can use a mutable lambda object.
auto func = [=]() mutable {
f(std::move(s));
};