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();
}