std::apply is mentioned in few stackoverflow answers and n3658, n3915 and usually defined as:
template <typename F, typename Tuple, size_t... I>
decltype(auto) apply_impl(F&& f, Tuple&& t, index_sequence<I...>) {
return forward<F>(f)(get<I>(forward<Tuple>(t))...);
}
template <typename F, typename Tuple>
decltype(auto) apply(F&& f, Tuple&& t) {
using Indices = make_index_sequence<tuple_size<decay_t<Tuple>>::value>;
return apply_impl(forward<F>(f), forward<Tuple>(t), Indices{});
}
However the reference implementation std::apply function fails to compile in such context (tested with clang 3.8 and gcc 5.2):
std::apply ([] (int&) {} , std::make_tuple (42));
One possible workaround is simply to remove std::forward<Tuple> from apply_impl but leave the universal reference intact:
template <typename F, typename Tuple, size_t... I>
decltype(auto) apply_impl(F&& f, Tuple&& t, index_sequence<I...>) {
return forward<F>(f)(get<I>(t)...);
}
Is there any drawbacks of this workaround? Is there more convenient solution possible?
UPDATE: Another possible workaround without changing std::apply (inspired by this SO answer):
template <typename T> constexpr T& make_tmp (T&& t) noexcept { return t; }
...
std::apply ([] (int& i) {} , make_tmp (std::make_tuple (42)));
Is it correct and result well defined?
When you have a temporary tuple containing a temporary object (or a reference to a temporary object), a function that demands a writable reference is right to reject the application. The function expects callers to pay attention to what it writes, and you are deterministicly discarding it.
Here is code that compiles:
int main() {
int i = 42;
std::apply ([] (int&) {} , std::tie(i) );
}
make_tuple creates a tuple of copies. apply then invokes the passed-in f on the soon to be discarded copies: these are quite properly treated as rvalues.
If you have an object which you want to pass by-reference through std::apply, put a reference to it in the tuple, not a copy of it. Then the rvalue-ness of the tuple within apply does not apply to reference contents.
For std::get<N>(some_tuple) to return an rvalue, either:
(A) the nth element must be a copy, and the tuple must be an rvalue
(B) the nth element must be an rvalue reference, and the tuple must be an rvalue reference
By storing an lvalue reference, std::get will never return an rvalue reference.
You probably want to call std::tie or std::forward_as_tuple depending on the situation. std::make_tuple is for when you want to create a copy in the tuple, not when you are storing references to other objects.
There are ways to allow you to pass rvalues as lvalues when you do want to discard any modifications, but those are usually bad ideas, and they should be done in client code with big warning stickers all over the place, not implicitly in a library.
template<class T>
T& as_lvalue( T&& t ) { return t; }
is a simple one.
Related
I require constexpr if in some part of my templated codebase, but since it is not available in C++11, I decided to come up with my own version of a simpler constexpr if-then-else.
Below is my implementation of constexpr if-then-else. I am not entirely sure if it is correct, and was unable to find any relevant content anywhere which suitably explains it. It would be really helpful if someone could verify this, and/or possibly point out alternative implementations.
template <typename T, typename F>
constexpr F static_ite(std::false_type, T &&, F &&f) { return f; }
template <typename T, typename F>
constexpr T static_ite(std::true_type, T &&t, F &&) { return t; }
template <bool cond, typename T, typename F>
constexpr auto static_ite(T &&t, F &&f)
-> decltype(static_ite(std::integral_constant<bool, cond>{}, std::forward<T>(t), std::forward<F>(f)))
{
return static_ite(std::integral_constant<bool, cond>{}, std::forward<T>(t), std::forward<F>(f));
}
I intend to use it as a generic template. Any help would be appreciated.
template <typename T, typename F>
constexpr typename std::decay<F>::type static_ite(std::false_type, T &&, F &&f) { return std::forward<F>(f); }
and similar for other branch. References can be passed through explicitly with std ref or pointers.
I find a more generic dispatch to be also useful:
template<std::size_t N, class...Ts>
nth_type<N,typename std::decay<Ts>::type...>
dispatch_nth(index_t<N>, Ts&&...ts);
(write obvious helpers).
This lets you work on more than 2 branches.
All of these become insanely more awesome with auto lambda paramerers; while that is c++14 it was implemented in most early c++1y early implementations.
I'm going to assume you want the function to return a reference to whatever you give it; if you instead want a copy, refer to Yakk's answer.
The return types of the first two overloads should be rvalue references, and you should std::forward when you return from them.
The long decltype could be shortened to typename std::conditional<cond, T &&, F &&>::type.
Everything else looks good to me.
The issue with this interface is that you are evaluating both sides of the conditional, so both sides need to be valid regardless of the value of the predicate. Because of this, it is more usual to pass in lambdas one of which will be invoked within the function. In addition, you may wish to pass in extra arguments so that when an expression is type-dependent you can ensure the type is only evaluated conditionally (though to use this effectively you may need C++14 generic lambdas).
Altogether, something like this:
template <bool cond, typename T, typename F, class... Args>
constexpr auto static_ite(T &&t, F &&f, Args&&... args)
-> typename std::result_of<typename std::conditional<cond, T, F>::type&&(Args&&...)>::type
{
return std::forward<typename std::conditional<cond, T, F>::type>(
std::get<cond ? 0 : 1>(std::make_tuple(std::ref(t), std::ref(f)))(
std::forward<Args>(args)...);
}
Note that this uses the std::tuple trick to bring everything within one function body; you can use your method of std::true_type / std::false_type overloading if you prefer.
Background: I have a function that I wrote that takes some callable object and a tuple, and unpacks the tuple into the a parameter pack that can be passed to the function. You can probably imagine that this involves a bit of template recursion. However, most of my colleagues aren't native speakers of C++, so they understandably have trouble with the code, as well as the compilation errors they get when the function is called incorrectly.
If I could just generate a pack of size_t from 0 to the size of the tuple - 1, I could use the following one-liner:
template <class Function, class... TupleArgs>
auto UnpackTuple(Function fn, std::tuple<TupleArgs>&& t)
-> decltype(fn(std::declval<TupleArgs>()...)) {
return fn(std::get<{my size_t pack}>(std::forward(t))...);
}
Is there anything I can substitute for "my size_t pack" to make this work? I know I could hack something together if I new that each type in TupleArgs was unique, but that's a very specific case that isn't useful to me.
Seems like you are looking for std::apply, which will do exactly what you want to do with UnpackTuple.
That being said, if you need to do this manually for some odd reason (you probably shouldn't, unless you want to do something entirely different from calling a function),
std::make_index_sequence does almost what you want (except that your size_t sequence is wrapped in a type, so you need an additional level of indirection). You can see how it is used in the example implementation of std::apply at cppreference.com:1)
template <class F, class Tuple, std::size_t... I>
constexpr decltype(auto) apply_impl(F&& f, Tuple&& t, std::index_sequence<I...>)
{
return std::invoke(std::forward<F>(f), std::get<I>(std::forward<Tuple>(t))...);
}
template <class F, class Tuple>
constexpr decltype(auto) apply(F&& f, Tuple&& t)
{
return apply_impl(
std::forward<F>(f),
std::forward<Tuple>(t),
std::make_index_sequence<std::tuple_size_v<std::remove_reference_t<Tuple>>>{}
);
}
Since you mentioned that you are still on C++14, you'll need to replace std::invoke with a normal function call (keep the std::forward, however, in case someone is mad enough to implement a operator() &&)
1) (CC-BY-SA 3.0)
Consider the following variadic template function:
template<typename T, typename... Args>
auto foo(Args... args) -> T[sizeof...(args)]
{
T t[sizeof...(args)];
// Maybe do some pack expansion/assignment
return t;
}
with the instantiation:
// total should be of type int[5];
auto total = foo(1,2,3,4,5);
I understand that this will not compile, due to the return type not being deducible, but I do not understand why it is not deducible.
Is there something about this function that the compiler does/can not know at compile time, or the order of parts of the function being evaluated, which prevents type deduction?
I suspect it is due to the calling of the function sizeof..., which I think evaluates at run-time. If so, is there a compile-time equivalent?
You cannot return built-in arrays by value. Use std::array instead.
Also, as it stands, you'll need to explicitly provide the type T as a template argument as it does not appear in the argument list. Thus the compiler has nothing to deduce it from.
Complete example:
#include <array>
template<typename T, typename... Args>
auto foo(Args... args) -> std::array<T, sizeof...(args)>
{
std::array<T, sizeof...(args)> t;
// Maybe do some pack expansion/assignment
return t;
}
int main () {
auto a = foo<int>(1,2,3);
}
Depending on the usecase, you may get rid of the explicit template argument by e.g. using the std::common_type of all elements in the pack or static_asserting they all have the same type and use that.
Also, for the record, sizeof... does yield a compile-time constant. The problem is, to make the answer explicit, that the compiler has no way tell what T is supposed to be.
Compiler unable to deduce T, because you don't pass T to function argument.
auto total = foo<int>(1,2,3,4,5);
If we correctly put T manually as template argument - it will compile & work fine on G++7.1
Or, you can simply decltype required type from variadic argument.
something like this:
template <typename T, typename...Args>
T getHead(T&& arg, Args&&...) {
return arg;
}
template<typename... Args>
auto foo(Args... args)
{
using TYPE = typename std::decay<decltype(getHead(args...))>::type;
return std::array<TYPE, sizeof...(Args)>{ args... };
}
And yes, use std::array, return local C-style array is undefined behaviour.
Consider the following code that build a class storing functions.
// Function list class
template <class... F>
struct function_list
{
template <class... G>
constexpr function_list(G&&... g) noexcept
: _f{std::forward<G>(g)...}
{
}
std::tuple</* F... OR F&&... */> _f;
};
// Function list maker
template <class... F, class R = /* Can we compute the return type here? */>
constexpr R make_function_list(F&&... f)
{
return function_list<
/* decltype(std::forward<F>(f))...
* OR F...
* OR F&&...
*/>(std::forward<F>(f)...);
}
I would like these functions to be perfectly forwarded (regardless of whether they are function pointers, functors, lambdas...). But I don't exactly understand all the type deduction happening behind std::forward and universal references. In the code above, I have three questions:
Should _f be of type std::tuple<F...> or std::tuple<F&&...> (and why?)
Is it possible to deduce the return type R in the template parameter list (because doing it manually instead of auto/decltype(auto) would be helpful to understand what is going on)
In the maker, what the function_list template argument should be: decltype(std::forward<F>(f)...), F, or F&&... (and why?)
Note: the constructor of function_list is not meant to be called directly, instead make_function_list is doing the job.
EDIT:
Is this case safe, when the operator() of function_list (not shown here) is not guaranted to be called on the same statement?
template <class... F>
constexpr function_list<F...> make_function_list(F&&... f)
{
return function_list<F&&...>(std::forward<F>(f)...);
}
But I don't exactly understand all the type deduction happening behind std::forward and universal references.
It's quite simple to understand via an example.
template <typename T>
void f(T&&)
{
std::tuple<T>{}; // (0)
std::tuple<T&&>{}; // (1)
}
In the case of (0):
T is deduced as T for rvalues
T is deduced as T& for lvalues.
In the case of (1):
T is deduced as T&& for rvalues
T is deduced as T& for lvalues.
As you can see, the only difference between two is how rvalues are deduced.
Regarding std::forward, this is what it does:
template <typename T>
void g(T&&);
template <typename T>
void f(T&& x)
{
g(x) // (0)
g(std::forward<T>(x)); // (1)
}
In the case of (0):
x is always an lvalue.
In the case of (1):
x is casted to T&& if T is deduced as T.
x stays an lvalue otherwise.
std::forward basically retains the type category of x by looking at how T was deduced.
Should _f be of type std::tuple<F...> or std::tuple<F&&...>
I think that in your case it should be std::tuple<F...>, as you want to store either lvalue references or values.
std::tuple<F&&...> would store either lvalue references or rvalue references - that would lead to dangling references in the case of temporaries.
Is it possible to deduce the return type R in the template parameter list
Yes, it is just function_list<F...>.
template <class... F, class R = function_list<F...>>
constexpr R make_function_list(F&&... f)
{
return function_list<F...>(std::forward<F>(f)...);
}
You don't even need the R template parameter.
template <class... F>
constexpr function_list<F...> make_function_list(F&&... f)
{
return function_list<F...>(std::forward<F>(f)...);
}
In the maker, what the function_list template argument should be: decltype(std::forward<F>(f)...), F, or F&&...
function_list should take F... as a template parameter for the reasons listed at the beginning of this answer (i.e. avoiding dangling references to temporaries).
It should still take std::forward<F>(f)... as its arguments to allow rvalues to be forwarded as such (i.e. moving rvalues into function_list's tuple).
If they are F&&, then if you pass a temporary to make_function_list, the returned class containing a tuple will store an rvalue reference to the temporary passed to make_function_list.
On the next line, it is now a dangling reference.
This seems bad in most use cases. This is not actually bad in all use cases; forward_as_tuple does this. But such use cases are not general use cases. The pattern is extremely brittle and dangerous.
In general, if you are returning a T&&, you want to return it as a T. This can cause a copy of the object; but the alternative is danging-reference-hell.
This gives us:
template<class... Fs>
struct function_list {
template<class... Gs>
explicit constexpr function_list(Gs&&... gs) noexcept
: fs(std::forward<Gs>(gs)...)
{}
std::tuple<Fs...> fs;
};
template<class... Fs, class R = function_list<Fs...>>
constexpr R make_function_list(Fs&&... fs) {
return R(std::forward<Fs>(fs)...);
}
Also make function_list's ctor explicit, because in the 1 argument case it devolves to a rather greedy implicit conversion constructor. This can be fixed but takes more effort than it is worth.
operator() requires an instance. A type name is not an instance.
It depends on what function_list is for. There basically are two cases:
function_list is a temporary helper that should never outlive the statement it appears in. Here we can store references to functions and perfect-forward each of them to the point of invocation:
template <class... F>
struct function_list
{
std::tuple<F&&...> f_;
// no need to make this template
constexpr function_list(F&&... f) noexcept
: f_{std::forward<F>(f)...}
{}
template <std::size_t i, typename... A>
decltype(auto) call_at(A&&... a)
{
return std::invoke(std::get<i>(f_), std::forward<A>(a)...);
}
};
function_list is a wrapper/container object akin to std::bind, in this case you'd want to store decayed copies of the functions to avoid dangling references and perfect-forwarding in this context would mean forwarding functions to the constructors of their decayed versions in f_ and then at the point of call imbuing the decayed functions with value category of the function_list itself:
template <class... F>
struct function_list
{
std::tuple<std::decay_t<F>...> f_;
template <typename... G>
constexpr function_list(G&&... g)
: f_{std::forward<G>(g)...}
{}
template <std::size_t i, typename... A>
decltype(auto) call_at(A&&... a) &
{
return std::invoke(std::get<i>(f_), std::forward<A>(a)...);
}
template <std::size_t i, typename... A>
decltype(auto) call_at(A&&... a) const&
{
return std::invoke(std::get<i>(f_), std::forward<A>(a)...);
}
template <std::size_t i, typename... A>
decltype(auto) call_at(A&&... a) &&
{
return std::invoke(std::get<i>(std::move(f_)), std::forward<A>(a)...);
}
template <std::size_t i, typename... A>
decltype(auto) call_at(A&&... a) const&&
{
return std::invoke(std::get<i>(std::move(f_)), std::forward<A>(a)...);
}
};
As with std::bind if you actually want to store a reference, you must do so explicitly with std::reference_wrapper.
Construction is the same in both cases:
template <class... F>
constexpr auto make_function_list(F&&... f)
{
return function_list<F...>(std::forward<F>(f)...);
}
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