Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Does this method of getting last element in template parameter pack have hidden overhead?

I have a set functions with different arguments, but all of them have last element the same. So I need to access that last argument in my function. Looking around I found quite some solutions with template specializations (here and here), but this solution came to my mind:

template<typename ...Args>
void function( Args&&... args )
{
     auto &last = std::get<sizeof...( Args ) - 1 >( std::tie( args... ) );
}

It may look obvious, but it was not so for me in the begining. 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?

like image 547
Slava Avatar asked Dec 10 '25 03:12

Slava


1 Answers

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();
}
like image 166
skypjack Avatar answered Dec 11 '25 23:12

skypjack



Donate For Us

If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!