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)
{
}
Related
I wrote a test framework for testing function output (see code below).
template <class FuncTy, class... IndexType>
typename std::enable_if<std::tuple_size<typename function_helper<FuncTy>::arguments>::value == sizeof...(IndexType)
and std::is_same_v<typename function_helper<FuncTy>::return_type, AnswerTy>
and not is_member_function_pointer_v<FuncTy>, void>::type
test(FuncTy func, IndexType... arg_indexes) {
using function_args_tuple = typename function_helper<FuncTy>::arguments;
using function_return_type = typename function_helper<FuncTy>::return_type;
// >>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>
function_args_tuple params; /// error is here
// <<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<
/// .....
}
It works well for functions that do not have references in arguments.
e.g.:
for fucntion int f(int x, vector<int> y); (i.e., no references in argument list), the type of params is a std::tuple<int, vector<int>>, so the params can instantiate normally with code above,
but for functions like int f(int& x, vector<int>& y); (i.e., one or more references in argument list), the type of params becomes std::tuple<int&, vector<int>&>, which is not able to instantiate with code above.
The type of the tuple params is affected by argument list of a function (done with another function), the type of the tuple is undefined, so I just cannot do like this and this, since both of the solutions in the two links use a make_tuple explictly and an instantiated tuple object.
So my question is how to remove the references without instantiate the tuple. (e.g., make tuple<int&, vector<int>&> to tuple<int, vector<int>>).
Or else if there is some ways to instantiate a tuple with references in the tuple's template argument list without hard-coded make_tuple calls.
You can get the element type of the tuple through template partial specialization and apply std::decay to those types to remove references, something like this
#include <type_traits>
#include <tuple>
template<class Tuple>
struct decay_args_tuple;
template<class... Args>
struct decay_args_tuple<std::tuple<Args...>> {
using type = std::tuple<std::decay_t<Args>...>;
};
Then you can get a tuple of the decay argument types through the helper class
decay_args_tuple<decltype(function_args_tuple)>::type params;
Note that the argument type is still required to be default constructible.
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.
I'd like to create template function that would create object basing on template typename and parameters pack.
I created a function that is supposed to create object based on typename from template, and I would also like to pass parameters pack to that template i order to pass parameters to constructor. Is this correct?:
template<typename TComponent, typename... Args>
void CreateComponent(Args... args)
{
std::shared_ptr<TComponent> component = std::make_shared<TComponent>(args ...);
}
I also wanted to pass those parameters to another fucntion like this:
template<typename TComponent, typename... Args>
void AddComponent(Args... args)
{
m_world->AddComponent<TComponent, Args>(m_id, args...);
}
But compiler returns an error " 'args' parameter pack must be expanded in this context"
Is it even possible to achieve what I want to achieve ?
But compiler returns an error " 'args' parameter pack must be expanded in this context"
Yes: you've forgotten to expand the types
m_world->AddComponent<TComponent, Args...>(m_id, args...);
// ...................................^^^
As pointed by Jarod42, according to the circumstances, you could avoid to explicit the Args... expansion
m_world->AddComponent<TComponent>(m_id, args...);
// no more Args...
and let the compiler deduce the types through args... (but we should see the AddComponent() definition).
Anyway, I don't see errors in your CreateComponents() function but, as correctly says François Andrieux in a comment, you don't using perfect forwarding.
It's a too-great argument to explain in an answer but, this way, you're renouncing to move semantics advantages (that is: you, potentially, make some unnecessary copies).
The following is your CreateComponents() function enabling perfect forwarding
template <typename TComponent, typename ... Args>
void CreateComponent (Args && ... args)
{ // .....................^^ forwarding reference added
std::shared_ptr<TComponent> component
= std::make_shared<TComponent>(std::forward<Args>(args)...);
} // ..............................^^^^^^^^^^^^^^^^^^^^^^^^
I have a function like this
template <typename... Args> void foo(Args&&... args);
to which I need to add an extra parameter at the end with a default argument. Since the pack needs to come last, I'm thinking of changing the function to
template <typename... Args> void foo(std::tuple<Args&&...> args,
const std::string& name = {});
The question is, what is the best way to pass the arguments in a tuple.
My understanding is that in the std::tuple<Args&&...> the Args are not forwarding references anymore, but strictly rvalue references. How do I get the forwarding references behavior for args wrapped in a tuple, e.g. accept an std::forward_as_tuple and preserve the reference types of the individual tuple elements. Also, what's the best way to pass the tuple here,
std::tuple<Args&&...> args
or
const std::tuple<Args&&...>& args
or
std::tuple<Args&&...>&& args
?
And do I need to use std::forward on the tuple elements inside the function, or simply std::get them?
My understanding is that in the std::tuple<Args&&...> the Args are not forwarding references anymore
Correct.
but strictly rvalue references
Yes, unless Args are specified explicitly, in which case reference collapsing can turn them into lvalue references, i.e., foo<int&>(...) will result in Args&& -> int& && -> int&.
what is the best way to pass the arguments in a tuple.
That depends on the intended usage of foo. If you don't need to know what Args... exactly are, you can probably get away with:
template <typename Tuple>
void foo(Tuple&& args, const std::string& name = {});
In such a case, individual types are still accessible using std::tuple_element_t<N, std::decay_t<Tuple>>.
If you do want to know Args... inside foo (without any additional levels of abstraction), you probably want to deduce the exact types, without any referenceness:
template <typename.... Args>
void foo(std::tuple<Args...>&& args, const std::string& name = {});
Note that if someone uses std::forward_as_tuple with lvalues and rvalues inside, the value categories will be stored in Args and you can still forward those arguments using std::forward (std::forward is not limited to forwarding references only, think of it as a conditional cast).
Also, what's the best way to pass the tuple here
Probably Tuple&& as suggested earlier. If not, then again it depends on the usage. If you use const std::tuple<Args...>&, then by looking at the list of overloads for std::get, you'll see that the the value category and constness propagates to the return value of std::get (modulo reference collapsing). The same is with std::tuple<Args...>&&. Also, using the latter, you will have to use a tuple rvalue as an argument (foo(std::forward_as_tuple(...), ...) as opposed to foo(my_tuple, ...)).
An alternative solution would be to accept a parameter pack, and detect whether the last parameter is something that can be bound by const std::string& or not:
#include <string>
#include <utility>
#include <tuple>
#include <type_traits>
struct dummy {};
template <typename... Args>
void foo_impl(Args&&... args)
{
const std::string& s = std::get<sizeof...(Args) - 1>(std::forward_as_tuple(std::forward<Args>(args)...));
}
template <typename... Args>
auto foo(Args&&... args)
-> std::enable_if_t<std::is_constructible<std::string, std::tuple_element_t<sizeof...(Args), std::tuple<dummy, Args...>>>{}>
{
foo_impl(std::forward<Args>(args)...);
}
template <typename... Args>
auto foo(Args&&... args)
-> std::enable_if_t<!std::is_constructible<std::string, std::tuple_element_t<sizeof...(Args), std::tuple<dummy, Args...>>>{}>
{
foo_impl(std::forward<Args>(args)..., "default");
}
DEMO
I'm trying to create a delayable call object. Something along the lines of (pseudo-code):
template <class FN>
struct delayable_call
{
return-type-of-FN call(); // <-- I'd like to use result_of here.
template<class ArgTypes...>
delayable_call(FN* pFn, ArgTypes... args);
FN* fn;
args-saving-struct;
};
I tried using result_of::type for the return type of call, but get errors during instantiation of the template because apparently the argument types need to be specified separately.
Instantiation:
int foo(bool, double); // function prototype.
delayable_call<int(bool, double)> delayable_foo(foo, false, 3.14); // instantiation
The error messages and documentation I've read about result_of seem to indicate that the argument types must also be specified. So instead of result_of<FN>::type, I'd need to specify result_of<FN(bool, double)>::type. This does actually fix the compilation problem I'm having, but breaks the generality of the template.
So, how can I use result_of with a template parameter when the template parameter represents the function signature?
template <class FN> struct delayable_call;
template<class R, class...Args> delayable_call<R(Args...)>{
typedef R(*)(Args...) pFN;
replace your delayable_call with a specialization, and you will extrace both R and Args.... You need Args... anyhow to store the parameters.
However, a library-strength delayable call will end up using type erasure. The easiest way is a simple std::function<R()> where you shove a lambda into it:
int foo(double);
double x = 7;
std::function<int()> delayed_foo = [x]{ return foo(x); }
and capture by value unless you really, really mean it to capture by reference.
You could deduce R via:
template<typename Fn, typename... Args>
std::function< typename std::result_of<Fn(Args...)>::type()>
make_delayed_call( Fn&& fn, Args&&... args ) {
return [=]{ return fn(std::move(args)...); }
}
which should deduce your R from the callable object and the arguments. This captures everything by copy -- capture by move requires either more boilerplate, or C++14.