我有一个具有不同参数的set函数,但它们都具有相同的最后一个元素。所以我需要在我的函数中访问最后一个参数。环顾四周,我找到了一些包含模板特化的解决方案(here和here),但我想到了这个解决方案:
template<typename ...Args>
void function( Args&&... args )
{
auto &last = std::get<sizeof...( Args ) - 1 >( std::tie( args... ) );
}
看起来很明显,但在开始时对我来说并非如此。它似乎简单而简短,但这种方法与模板特化和辅助函数/类的经典解决方案相比有任何隐藏的开销吗?
答案 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