How to change the last argument in the parameter pack?
You might forward your arguments as tuple and then unpack all except the last one using std::integer_sequence
. This code looks much simpler than your approach:
template<typename... Args>
void f1(Args... args)
{
boost::fusion::vector<Args...> v(args...);
std::cout << boost::fusion::accumulate(v, 0, [](auto i1, auto i2) { return i1 + i2; }) << std::endl;
}
template<typename Tuple, size_t... idx>
void callImpl(Tuple&& tuple, std::index_sequence<idx...>)
{
f1(std::get<idx>(std::forward<Tuple>(tuple))..., 10);
}
template<typename... Ts>
void callWithLast10(Ts&&... ts)
{
callImpl(std::forward_as_tuple(ts...), std::make_index_sequence<sizeof...(Ts) - 1>());
}
Usage:
f1(1, 2, 3, 4); // Prints 10
callWithLast10(1, 2, 3, 4); // Prints 16
Does this method of getting last element in template parameter pack have hidden overhead?
It seems to be simple and short, but does this method have any hidden overhead versus classic solutions with template specialization and helper functions/classes?
It has a drawback that could be annoying in some cases. If your type has reference qualifiers on member methods, you can encounter problems by getting an lvalue reference out of it.
Let's consider the following example:
#include<utility>
#include<tuple>
struct S {
void foo() && {}
};
template<typename T>
void f(T &&t) {
std::forward<T>(t).foo();
}
int main() {
f(S{});
}
Everything works fine for we have originally an rvalue reference and by forwarding the forwarding reference we can safely call the foo
member method.
Let's consider now your snippet (note, the following code doesn't compile - continue reading):
#include<utility>
#include<tuple>
struct S {
void foo() && {}
};
template<typename... T>
void g(T&&... t) {
auto &last = std::get<sizeof...(T) - 1 >(std::tie(t...));
last.foo();
}
int main() {
g(S{});
}
This won't compile for foo
cannot be invoked anymore on a variable having type S &
because of the reference qualifier.
On the other side, by forwarding and extracting the last parameter somehow you can keep intact its type and you don't have such a problem.
As an example:
template<typename... T>
void g(T&&... t) {
std::get<sizeof...(T) - 1>(std::forward_as_tuple(std::forward<T>(t)...)).foo();
}
Get Last element of parameter pack in C++17 / C++20
Wrapping args
in any function call would remove the warning.
You could make an identity function specifically for this purpose, but you might as well use std::forward
and get proper forwarding as a bonus.
template<typename... Args>
decltype(auto) last(Args&&... args){
return (std::forward<Args>(args), ...);
}
Is there a way to remove the last template parameter of variadic template methods?
Here's a C++11 implementation depending only on std::string
and std::unordered_map
. Some mandatory remarks:
- As mentioned, this is extremely brittle due to inferring the function type by the provided arguments. This is UB waiting to happen.
method
really shouldn't be a pointer.- If your return type is not assignable, this will break spectacularly.
- The class pointer really should be a reference instead.
- If you think the implementation is insane, then yes, it is indeed, and you should give up on being fully generic.
A C++11 implementation of std::index_sequence
and friends can be found here.
See it in action.
template<typename...>
struct typelist {};
template<size_t, typename, typename, typename, typename>
struct call;
template<size_t N, typename R, typename C, typename... Accum, typename Head, typename... Tail>
struct call<N, R, C, typelist<Accum...>, typelist<Head, Tail...>>
: call<N, R, C, typelist<Accum..., Head>, typelist<Tail...>>
{
};
template<typename R, typename C, typename... Accum, typename Head, typename... Tail>
struct call<sizeof...(Accum), R, C, typelist<Accum...>, typelist<Head, Tail...>>
{
template<typename... Ts>
int operator()(Ts&&...)
{
return 0;
}
template<typename... Ts>
int operator()(R& ret, void (EmptyClass::* g)(), C& obj, Accum&... args, Ts&&...)
{
auto f = (R (C::*)(Accum...))g;
ret = (obj.*f)(std::move(args)...);
return 0;
}
};
template<typename R, typename C, typename... Args, size_t... Is>
R switcher(int i, index_sequence<Is...>, void (EmptyClass::* g)(), C& obj, Args&... args)
{
R ret{};
int unused[] = {(i == Is ?
call<Is, R, C, typelist<>, typelist<Args..., void>>{}(ret, g, obj, args...)
: 0)...};
(void)unused;
return ret;
}
template<typename C, typename R, typename... Args>
void reg(std::string name, R (C::* func)(Args... args)) {
(*methods)[name] = std::make_pair((void (EmptyClass::*)())func, sizeof...(Args));
}
template<typename R, typename C, typename... Args>
R exec(C* obj, std::string name, Args... args) {
if(obj == nullptr)
throw "a tantrum";
auto& info = (*methods)[name];
auto g = info.first;
size_t i = info.second;
if(i > sizeof...(Args))
throw "a fit";
return switcher<R>(i, make_index_sequence<sizeof...(Args) + 1>{}, g, *obj, args...);
}
Is there any way to access everything except the last template parameter?
You could go with the standard method of using std::index_sequence
template<template<auto...> typename Tmp, size_t... Is, typename... Args>
constexpr auto take_as(std::index_sequence<Is...>, Args...)
{
using Tup = std::tuple<Args...>;
return Tmp<std::tuple_element_t<Is, Tup>{}...>{};
}
template<auto... Vals>
struct except_last
{
template<template<auto...> typename Tmp>
using as = decltype(take_as<Tmp>(std::make_index_sequence<sizeof...(Vals) - 1>{},
std::integral_constant<decltype(Vals), Vals>{}...));
};
Which you use as
using F = except_last<1, 2, 3, 4>::as<Foo>; // F is Foo<1, 2, 3>
This is both easier to implement and read, but you potentially get O(n) instantiation depth. If you are obsessed with efficiency, you could do O(1) instantiation depth by abusing fold expressions
template<typename T>
struct tag
{
using type = T;
};
template<typename F, typename... Ts>
using fold_t = decltype((F{} + ... + tag<Ts>{}));
template<size_t N, typename... Ts>
struct take
{
template<typename T>
auto operator+(tag<T>) -> take<N - 1, Ts..., T>;
};
template<typename... Ts>
struct take<0, Ts...>
{
template<template<auto...> typename Tmp>
using as = Tmp<Ts{}...>;
template<typename T>
auto operator+(tag<T>) -> take<0, Ts...>;
};
template<auto... Vals>
struct except_last
{
template<template<auto...> typename Tmp>
using as = fold_t<take<sizeof...(Vals) - 1>,
std::integral_constant<decltype(Vals), Vals>...>::template as<Tmp>;
};
how to return the last type of a variadic template?
You could do a template recursion as below:
template<typename T, typename... Ts>
struct LastTypeOfTs {
typedef typename LastTypeOfTs<Ts...>::type type;
};
template<typename T>
struct LastTypeOfTs<T> {
typedef T type;
};
template<typename... Ts>
typename LastTypeOfTs<Ts...>::type f() {
//...
}
LIVE DEMO
How to overload variadic templates when they're not the last argument
When a parameter pack doesn't appear last in the parameter declaration, it is a non-deduced context. A non-deduced context means that the template arguments have to be given explicitly. This is why foo
#1 is a better overload. You can force the second overload call by providing explicit arguments (foo<int,int>(1,2,3)
) or as you said, move the int
to the front.
To make things clear, you can overload a function with variadic templates, but when they do not appear as the last argument, they cannot be deduced, which automatically disqualifies them as candidates when explicit arguments are not provided. When they are provided, the template parameters are replaced with their provided types and the resulting non-template function is a candidate in overload resolution.
To answer your question, you can put all arguments into a tuple and pick out the last and test that one. Then pass on an overload based on a simple is_same
check:
template<class...Us>
void foo_impl(true_type,Us...); // last argument is int
template<class...Us>
void foo_impl(false_type,Us...); // last argument non-int
template<class...Us>
void foo( Us&&...us ) {
using tuple=tuple<Us...>;
using last=decltype(get<sizeof...(Us)-1>(declval<tuple>()));
foo_impl(is_same<decay_t<last>,int>{}, forward<Us>(us)...);
}
Obtain all-but-last parameter of variadic template
Do you like tuples?
How do you like forward as tuple?
struct foo {
template<class...Ts>
foo(Ts&&...ts):
foo(
magic<0>{}, // sent it to the right ctor
std::index_sequence< sizeof...(ts)-1 >{}, // the last shall be first
std::make_index_sequence<sizeof...(ts)-1>{}, // the first shall be last
std::forward_as_tuple(std::forward<Ts>(ts)...) // bundled args
)
{}
private:
template<size_t>
struct magic {};
template<size_t...I0s, size_t...I1s, class...Ts>
foo(
magic<0>, // tag
std::index_sequence<I0s...>, // first args
std::index_sequence<I1s...>, // last args
std::tuple<Ts...> args // all args
):
foo(
magic<1>{}, // dispatch to another tagged ctor
std::get<I0s>(std::move(args))..., // get first args
std::get<I1s>(std::move(args))... // and last args
)
{}
// this ctor gets the args in an easier to understand order:
template<class...Coords>
foo(magic<1>, std::vector<T> values, Coords...coords) {
}
};
here the public ctor packs up the arguments into a tuple of references (possibly l, possibly r). It also gets two sets of indexes.
It then sends it to magic<0>
ctor, which shuffles the arguments around so that the last one is first (assuming the indexes are right).
The magic<1>
ctor gets the vector first, then the coords.
Basically I shuffled the arguments around so the last one became first.
magic
just exists to make me not have to think much about overload resolution, and make which ctor I'm forwarding to explicit. It would probably work without it, but I like tagging when I'm doing something crazy with ctor forwarding.
Related Topics
Handling Header Files Dependencies with Cmake
Can Openssl on Windows Use the System Certificate Store
Generating M Distinct Random Numbers in the Range [0..N-1]
Multiple Definition Error Including C++ Header File with Inline Code from Multiple Sources
Why Is Std::Fill(0) Slower Than Std::Fill(1)
Difference Between Long and Int Data Types
C++ - Arguments for Exceptions Over Return Codes
How to Use an Array as Map Value
C++: When (And How) Are C++ Global Static Constructors Called
Accessing Protected Members of Superclass in C++ with Templates
What Destructors Are Run When the Constructor Throws an Exception
Why Aren't Static Const Floats Allowed
Converting Std::String to Std::Vector<Char>
Using Vector<Char> as a Buffer Without Initializing It on Resize()