I have a builder class that I'd like to store arguments as references for use in subsequent building.
I'd like to pass in a variable number of arguments to my class, infer the template arguments using class template argument deduction, and store those passed arguments as references in a std::tuple.
What's the easiest way to convert from a parameter pack to a std::tuple of references?
I found std::forward_as_tuple which does something similar to what I want, but I don't want a forwarding reference plus it gives a syntax error during initialization of the member tuple.
template <typename... Ts>
struct Builder
{
using ArgsT = decltype(std::forward_as_tuple(Ts{}...));
ArgsT args_;
Builder(Ts&... ts) : args_(ts...) {}
};
int main()
{
struct Foo
{
int a;
double b;
};
Foo foo{};
Builder fooBuilder{foo.a, foo.b};
}
The syntax error is:
error: no matching function for call to std::tuple<int&&, double&&>::tuple(int&, double&)
If you simply wants reference, use them directly:
template <typename... Ts>
struct Builder
{
using ArgsT = std::tuple<Ts&...>;
ArgsT args_;
Builder(Ts&... ts) : args_(ts...) {}
};
int main()
{
struct Foo
{
int a;
double b;
};
Foo foo{};
Builder fooBuilder{foo.a, foo.b};
}
For your code:
decltype(std::forward_as_tuple(Ts{}...)) resolves in std::tuple<Ts&&...>.
Ts{} creates a temporary (and requires your type to be default constructible).
And you cannot bind int& to int&&.
You might use decltype(std::forward_as_tuple(std::declval<Ts&>()...)) which resolves in std::tuple<Ts&...>, but later is simpler and provided solution;-).
Another way:
#include <tuple>
template<typename Tuple>
struct Builder
{
Tuple args_;
Builder(Tuple const& t) : args_(t) {}
};
int main()
{
struct Foo
{
int a;
double b;
};
Foo foo{};
Builder fooBuilder{std::tie(foo.a, foo.b)};
}
Builder(Ts&... ts) is a pack of lvalue references.
Ts{}... is a pack of prvalues of the same type.
std::forward_as_tuple(Ts{}...) is a tuple containing rvalue references to the same type.
lvalue references and rvalue references are not the same thing; you cannot assign one to the other. So args_(ts...) generates the appropriate error message.
There is more than one way to produce a solution to what could be your problem.
template <typename... Ts>
struct Builder
{
using ArgsT = std::tuple<Ts&&...>; // (1)
using ArgsT = std::tuple<Ts&...>; // (2)
using ArgsT = std::tuple<Ts...>; // (3)
all 3 of these are actually reasonable ways to solve your problem, depending on later code options. Pick one depending on what your real problem is.
ArgsT args_;
What goes here:
Builder(Ts&&... ts) : args_(std::forward<Ts>(ts)...) {}
Builder(Ts&... ts) : args_(ts...) {}
Builder(Ts&&... ts) : args_(std::forward<Ts>(ts)...) {}
for each of the above 3 cases.
In case (1) you perfect forward your arguments into a tuple, and the tuple stores rvalue references to any rvalue arguments. In case (2) you only take lvalue arguments, and you store a tuple of lvalue references to them. In case (3) you perfect forward your arguments into a tuple, and store values if the argument was an rvalue and lvalue references it he value was an lvalue.
(3) is useful when you want your references to outlive the current line; the only safe way to do that is to store a copy, and then move into it.
(2) is useful if you only want to refer to lvalues.
(1) is useful if your Builder object is not going to outlive the current line, and you don't want to pay for even moving objects. It is a tad more dangerous than (1).
All 3 will make your sample code compile.
Related
I have a question about the code written in https://koturn.hatenablog.com/entry/2018/06/10/060000
When I pass a left value reference, if I do not remove the reference with std::decay_t, I get an error.
Here is the error message
'error: 'operator()' is not a member of 'main()::<lambda(auto:11, int)>&
I don't understand why it is necessary to exclude the left value reference.
I would like to know what this error means.
#include <iostream>
#include <utility>
template <typename F>
class
FixPoint : private F
{
public:
explicit constexpr FixPoint(F&& f) noexcept
: F{std::forward<F>(f)}
{}
template <typename... Args>
constexpr decltype(auto)
operator()(Args&&... args) const
{
return F::operator()(*this, std::forward<Args>(args)...);
}
}; // class FixPoint
namespace
{
template <typename F>
inline constexpr decltype(auto)
makeFixPoint(F&& f) noexcept
{
return FixPoint<std::decay_t<F>>{std::forward<std::decay_t<F>>(f)};
}
} // namespace
int
main()
{
auto body = [](auto f, int n) -> int {
return n < 2 ? n : (f(n - 1) + f(n - 2));
};
auto result = makeFixPoint(body)(10);
std::cout << result << std::endl;
}
I don't understand why it is necessary to exclude the left value
reference. I would like to know what this error means.
When you pass an lvalue lambda into makeFixPoint(), the template parameter F of makeFixPoint() is instantiated as L&, where L is the lambda type. In the function body, FixPoint<std::decay_t<F>>{...} will be instantiated as FixPoint<L>{...}, so the constructor of FixPoint is instantiated as
explicit constexpr FixPoint(L&& f);
which accepts a lambda type of rvalue reference. In makeFixPoint(), if you initialize it with {std::forward<F>(f)} i.e. {std::forward<L&>(f)}, f will be forwarded as an lvalue, which will be ill-formed since rvalue reference cannot bind an lvalue.
The purpose of using {std::forward<std::decay_t<F>>(f)} is to force f to be forwarded as an rvalue.
,The code you have shared is toxic.
template <typename F>
class FixPoint : private F
{
public:
explicit constexpr FixPoint(F&& f)
what this means is that we expect F to be a value type (because inheriting from a reference isn't possible). In addition, we will only accept rvalues -- we will only move from another F.
: F{std::forward<F>(f)}
{}
this std::forward<F> is pointless; this indicates that the person writing this code thinks they are perfect forwarding: they are not. The only legal types F are value types (not references), and if F is a value type F&& is always an rvalue reference, and thus std::forward<F> is always equivalent to std::move.
There are cases where you want to
template<class X>
struct wrap1 {
X&& x;
wrap1(X&& xin):x(std::forward<X>(xin)){}
};
or even
template<class X>
struct wrap2 {
X x;
wrap2(X&& xin):x(std::forward<X>(xin)){}
};
so the above code is similar to some use cases, but it isn't one of those use cases. (The difference here is that X or X&& is the type of a member, not a base class; base classes cannot be references).
The use for wrap2 is when you want to "lifetime extend" rvalues, and simply take references to lvalues. The use for wrap1 is when you want to continue the perfect forwarding of some expression (wrap1 style objects are generally unsafe to keep around for more than a single line of code; wrap2 are safe so long as they don't outlive any lvalue passed to them).
template <typename F>
inline constexpr decltype(auto)
makeFixPoint(F&& f) noexcept
{
return FixPoint<std::decay_t<F>>{std::forward<std::decay_t<F>>(f)};
}
Ok more red flags here. inline constexpr is a sign of nonsense; constexpr functions are always inline. There could be some compilers who treat the extra inline as meaning something, so not a guarantee of a problem.
return FixPoint<std::decay_t<F>>{std::forward<std::decay_t<F>>(f)};
std::forward is a conditional move. It moves if there is an rvalue or value type passed to it. decay is guaranteed to return a value type. So we just threw out the conditional part of the operation, and made it into a raw move.
return FixPoint<std::decay_t<F>>{std::move(f)};
this is a simpler one that has the same meaning.
At this point you'll probably see the danger:
template <typename F>
constexpr decltype(auto)
makeFixPoint(F&& f) noexcept
{
return FixPoint<std::decay_t<F>>{std::move(f)};
}
we are unconditionally moving from the makeFixPoint argument. There is nothing about the name makeFixPoint that says "I move from the argument", and it accept forwarding references; so it will silently consume an lvalue and move-from it.
This is very rude.
So a sensible version of the code is:
template <typename F>
class FixPoint : private F
{
public:
template<class U,
class dU = std::decay_t<U>,
std::enable_if_t<!std::is_same_v<dU, FixPoint> && std::is_same_v<dU, F>, bool> = true
>
explicit constexpr FixPoint(U&& u) noexcept
: F{std::forward<U>(u)}
{}
[SNIP]
namespace
{
template <typename F>
constexpr FixPoint<std::decay_t<F>>
makeFixPoint(F&& f) noexcept
{
return FixPoint<std::decay_t<F>>{std::forward<F>(f)};
}
} // namespace
that cleans things up a bit.
The template just can't work as intended this way.
First, because it is supposed to be possible to inherit from F, it doesn't make much sense to use std::decay_t. Instead it is probably supposed to be just std::remove_cvref_t, but before C++20 that was kind of cumbersome to write and std::decay_t does almost the same.
In the class template F is intended to be a non-reference type. It is not a template parameter of the constructor. So it cannot be used for perfect-forwarding. The constructor argument will always be a rvalue reference which cannot take lvalues.
Even if a lvalue is passed to makeFixPoint, the call std::forward<std::decay_t<F>>(f) forces conversion to a rvalue reference, which then can be used in the constructor. That is clearly dangerous. The function always behaves as if you had std::moved the argument into it.
It should just be std::forward<F>(f) and the constructor should take another template parameter that can be used to form a forwarding reference:
template<typename G>
requires std::is_same_v<std::remove_cvref_t<G>, F>
explicit constexpr FixPoint(G&& g) noexcept
: F{std::forward<G>(g)}
{}
I have a function that accepts variadic arguments packed into a tuple
template <class... Args>
void Bottom(tuple<Args&&...> t)
{
}
Is Args&& a forwarding reference? I.e. would the rules for reference collapsing apply or am I just appending && to every argument in the pack?
And say I want to call this function from a function that certainly gets forwarding references:
template <class... Args>
void Top(Args&&... args) {
// 1. Bottom<Pack...>();
// 2. Bottom<Pack&&...>();
}
which syntax would be best if I don't want to alter the arguments, 1 or 2?
EDIT
I'm only using tuple to showcase a class that packs my parameters. The actual packing classes vary in different levels of the call hierarchy. The idea of using using_fwd_as_tuple is cool as a resource to find what the library does at this case.
I'd say none. I'd use std::forward_as_tuple and let compiler do the deduction:
template <class... Args>
void Top(Args&&... args) {
Bottom(std::forward_as_tuple(args...));
}
No, tuple<Args&&...> t are not forwarding references. They can only be present as top-level arguments.
You are not appending anything, you are attempting to match the arguments. Such function only accepts tuples (by value) which contain r-value references.
Example
#include <tuple>
using namespace std;
template <class... Args>
void Bottom(tuple<Args&&...> t)
{
}
// Type your code here, or load an example.
int main(){
double var=0.0;
tuple<int,double&,char&&> tup1{1,var,'c'};
//#1
//Bottom(tup1);
tuple<int&&,double&&,char&&> tup2{1,0.0,'c'};
//#2
//Bottom(tup2);
//#3
Bottom(std::move(tup2));
}
Does not compile since the arguments cannot be matched.
Does not compile either. Eventhough the arguments do match, the tuple itself is passed by value, in this case by copy and the copy constructor is deleted in presence of r-value tuple members.
Moving is fine, this instantiates this template:
template<>
void Bottom<int, double, char>(std::tuple<int &&, double &&, char &&> t)
{
}
Im my program I have template classes that are mostly wrappers for a special purpose std::function<..>. The minimal example is:
template <typename... Args>
class Foo {
public:
explicit Foo(std::function<void(Args&&...)> _function)
: function_(_function)
{}
template<typename... Arguments>
void Bar(Arguments&&... _args) {
function_(std::forward<Arguments>(_args)...);
}
private:
std::function<void(Args&&...)> function_;
};
Instantiations of these templates are usually a combination of l-value ref, r-value ref or no-ref types. The problem is that calling Bar leeds to errors when some of the arguments are non-ref types such as an int or std::vector. The work around is to declare a temp variable and then move it into the function call.
int main(){
Foo<int> test1([](int x) { });
const int x = 1;
test1.Bar(x); // [Error] cannot bind rvalue reference of type 'int&&' to lvalue of type 'const int'
int tmp = x;
test1.Bar(tmp); // [Error] cannot bind rvalue reference of type 'int&&' to lvalue of type 'int'
test1.Bar(std::move(tmp)); // [OK] But I don't want to have to reassign and move every time I use this.
/* I want perfect forwarding on variables that can be forwarded. */
/* There are cases when the templates are like this with a combination of l-value ref and r-value ref and non-ref types. */
Foo<const std::vector<uint8_t>&, std::vector<uint8_t>&&, int> test2([](const std::vector<uint8_t>&, std::vector<uint8_t>&&, int) { });
test2.Bar(std::vector<uint8_t>(1, 2), std::vector<uint8_t>(1, 2), x); // [Error] cannot bind rvalue reference of type 'int&&' to lvalue of type 'const int'
return 1;
}
I want to be able to use Bar with any template parameter without having to re-assign and std::move() everytime, but also have ref parameters perfectly forwarded. Is there a way to do this?
EDIT
After looking around the web for a bit - The problem is std::function<void(Args&&...)> function_; is not a function that takes a universal ref but instead takes an r-val ref. So trying to forward no-ref types throws an error.
So the question then is, is it possible to have and store a std::function that takes a universals references?
In std::function<void(Args&&...)>, you actually expect r-value reference, you probably want std::function<void(Args...)>:
template <typename... Args>
class Foo {
public:
explicit Foo(std::function<void(Args...)> _function)
: function_(_function)
{}
template <typename... Arguments>
void Bar(Arguments&&... _args) {
function_(std::forward<Arguments>(_args)...);
}
private:
std::function<void(Args...)> function_;
};
Demo
If appropriate, you may get rid of std::function:
template <typename F>
class Foo {
public:
explicit Foo(F f) : f(f) {}
template <typename... Ts>
auto operator ()(Ts&&... args) const
-> decltype(f(std::forward<Ts>(args)...))
{
return f(std::forward<Ts>(args)...);
}
private:
F f;
};
template <typename F>
Foo<F> MakeFoo(F f) { return Foo<F>{f}; }
Demo
I would like to have the following code in c++17:
#include <iostream>
#include <string>
#include <type_traits>
#include <functional>
class Foo;
template<class T>
class Bar {
public:
std::function<T(Foo&)> m_fn;
template<class Fn>
Bar(Fn fn) : m_fn(fn) {};
T thing(Foo &foo) const {
return m_fn(foo);
}
};
template<class Fn>
Bar(Fn) -> Bar<decltype(std::invoke(std::declval<Fn>(),
std::declval<Foo&>()))>;
class Foo {
public:
Foo() {};
template<class T>
std::vector<T> do_thing(const Bar<T> &b) {
std::vector<T> r;
r.push_back(b.thing(*this));
return r;
}
};
std::string test(Foo &) {
return "hello";
}
int main() {
Foo foo = Foo();
// works
std::vector<std::string> s = foo.do_thing(Bar{test});
// cant deduce T parameter to do_thing
std::vector<std::string> s = foo.do_thing({test});
}
But compiling this gives me "couldn't deduce template parameter âTâ" on the call to do_thing.
Having do_thing(Bar{test}) fixes this and works fine but equates to some ugly code in the real code equivalent. I would like to have do_thing({test}) or do_thing(test) implicitly construct a Bar and pass that as the argument if possible.
I also don't want to forward declare a variable to pass into do_thing either
Is there some way to guide the inference of template argument T so that the call to do_thing can stay clean?
Edit:
Sorry for the late edit, but the arguments to the Bar constructor are over simplified in the example I included. In reality, there is an extra parameter std::optional<std::string> desc = std::nullopt and that might change in the future (although unlikely). So constructing the Bar inside do_thing would be a bit hard to maintain...
would like to have do_thing({test}) or do_thing(test) implicitly construct a Bar and pass that as the argument if possible.
Unfortunately, when you call do_thing({test}) or do_thing(test), test (or {test}) isn't a Bar<T> object. So the compiler can't deduce the T type and can't construct a Bar<T> object.
A sort of chicken-and-egg problem.
The best I can imagine is to add, in Foo, a do_test() method as follows
template<typename T>
auto do_thing (T const & t)
{ return do_thing(Bar{t}); }
This way you can call (without graphs)
std::vector<std::string> s = foo.do_thing(test);
You get the same result as
std::vector<std::string> s = foo.do_thing(Bar{test});
-- EDIT --
The OP ask
is there any way of preserving the {test} brace syntax? maybe with initializer_list or something?
Yes... with std::initializer_list
template<typename T>
auto do_thing (std::initializer_list<T> const & l)
{ return do_thing(Bar{*(l.begin())}); }
but, this way, you accept also
std::vector<std::string> s = foo.do_thing(Bar{test1, test2, test3});
using only test1
Maybe a little better... another way can be through a C-style array
template <typename T>
auto do_thing (T const (&arr)[1])
{ return do_thing(arr[0]); }
This way you accept only an element.
This happens because {} is not an expression and can only be used in limited ways while doing argument deduction, the parameter must have specific forms in order to succeed.
The allowed parameters types that can be used to deduce template parameters when {} is involved are better expanded in [temp.deduct.call]/1, two of the examples extracted from the cited part of the standard are:
template<class T> void f(std::initializer_list<T>);
f({1,2,3}); // T deduced to int
template<class T, int N> void h(T const(&)[N]);
h({1,2,3}); // T deduced to int
In your example the deduction guide is not used to deduce the T for {test} for the same as above.
foo.do_thing(Bar{test});
is your direct option without using additional functions.
Reading about universal references led me to wonder: how can I construct a class template such that it stores by reference if possible, or by value if it must?
That is, can I do something like this
template <class T>
class holder {
T obj_m; // should be a reference if possible...
public:
holder(T t) :obj_m { t } {}
}
auto
hold_this(T && t) { return holder<T>(t); }
Except that when hold_this() is given an lvalue the holder will hold a reference, and when given an rvalue the holder will make a copy?
Except that when hold_this() is given an lvalue the holder will hold a reference, and when given an rvalue the holder will make a copy?
You already wrote it (minus the required template <typename T>). The deduction rules for a forwarding reference preserve value category as follows:
If t is bound to an lvalue of type T2, then T = T2&.
If t is bound to an rvalue of type T2, then T = T2.
It's those deduction rules that std::forward relies on to do its job. And why we need to pass the type to it as well.
The above means that you instantiate holder directly with T2 in the rvalue case. Giving you exactly what you want. A copy is made.
As a matter of fact, two copies are made. Once to create the constructor argument t, and the other copy is to initialize obj_m from it. But we can get rid of it with some clever use of type_traits:
template <class T>
class holder {
T obj_m; // should be a reference if possible...
public:
holder(std::add_rvalue_reference_t<T> t) :obj_m { std::forward<T>(t) } {}
};
template<typename T>
auto hold_this(T && t) { return holder<T>(std::forward<T>(t)); }
See it live. We use add_rvalue_reference_t to make t be of the correct reference type in each case. And "simulate" the argument deduction which would make obj_m { std::forward<T>(t) } resolve to initializing obj_m from the correct reference type.
I say "simulate" because it's important to understand the constructor argument for holder cannot be a forwarding reference because the constructor itself is not templated.
By the way, since you tagged c++17, we can also add a deduction guide to your example. If we define it as follows (with the feedback from T.C. incorporated):
template <class T>
class holder {
T obj_m; // should be a reference if possible...
public:
holder(T&& t) :obj_m { std::forward<T>(t) } {}
};
template<typename T>
holder(T&&) -> holder<T>;
Then this live example shows you can define variables as hold h1{t}; and hold h2{test()};, with the same deduced types as the function return values from before.