这种在模板参数包中获取最后一个元素的方法是否有隐藏的开销?

时间:2017-08-24 17:41:36

标签: c++ c++11 variadic-templates variadic-functions

我有一个具有不同参数的set函数,但它们都具有相同的最后一个元素。所以我需要在我的函数中访问最后一个参数。环顾四周,我找到了一些包含模板特化的解决方案(herehere),但我想到了这个解决方案:

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

看起来很明显,但在开始时对我来说并非如此。它似乎简单而简短,但这种方法与模板特化和辅助函数/类的经典解决方案相比有任何隐藏的开销吗?

2 个答案:

答案 0 :(得分:2)

  

它似乎简单而简短,但这种方法与模板特化和辅助函数/类的经典解决方案相比有任何隐藏的开销吗?

它有一个缺点,在某些情况下可能会很烦人。如果你的类型在成员方法上有引用限定符,你可以通过从中获取左值引用来遇到问题。

让我们考虑以下示例:

#include<utility>
#include<tuple>

struct S {
    void foo() && {}
};

template<typename T>
void f(T &&t) {
    std::forward<T>(t).foo();
}

int main() {
    f(S{});
}

一切正常,因为我们原来是一个右值引用,通过转发转发引用,我们可以安全地调用foo成员方法。

现在让我们考虑你的代码片段(请注意,以下代码不会编译 - 继续阅读):

#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{});
}

由于引用限定符,无法再对类型为foo的变量调用S &的{​​{1}}编译。

另一方面,通过转发和提取最后一个参数,你可以保持其完整的类型并且你不会遇到这样的问题。
举个例子:

template<typename... T>
void g(T&&... t) {
    std::get<sizeof...(T) - 1>(std::forward_as_tuple(std::forward<T>(t)...)).foo();
}

答案 1 :(得分:1)

这取决于您的实现和编译器标志。在godbolt.org上试一试。 gcl 7.2 with -O2似乎能够在我尝试使用它时完全优化它:

#include <tuple>

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

int main()
{
    volatile auto x = function(1, 1., 1.f);
}

它产生了这个:

main:
  movss xmm0, DWORD PTR .LC0[rip]
  xor eax, eax
  movss DWORD PTR [rsp-4], xmm0
  ret
.LC0:
  .long 1065353216

example