c++:可变模板和函数重载

c++ : variadic template and function overloading

本文关键字:函数 重载 c++      更新时间:2023-10-16

请参阅下面的示例:https://onlinegdb.com/Hkg6iQ3ZNI

#include <iostream>
#include <utility>
#include <type_traits>
class A 
{
public:
A(int v=-10):v_(v){}
void print()
{
std::cout << "called A: " << v_ << std::endl;
}
private:
int v_;
};
void f(int v)
{
std::cout << "called f: " << v << std::endl;

}

template<typename T,typename ... Args>
void run(A&& a,
T&& t,
Args&& ... args)
{
a.print();
t(std::forward<Args>(args)...);
}

template<typename T,typename ... Args>
void run(T&& t,
Args&& ... args)
{
run(A(),
std::forward<T>(t),
std::forward<Args>(args)...);
}
int main()
{
int v_function=1;
int v_a = 2;

run(f,v_function);

return 0;
}

上面的代码编译、运行和打印(如预期(:

称为A:-10

称为f:1

但如果主功能修改为:

int main()
{
int v_function=1;
int v_a = 2;

run(f,v_function);

// !! added lines !!
A a(v_a);
run(a,f,v_function);

return 0;
}

则编译失败并出现错误:

main.cpp:30:6:错误:对"(A((void(&((int(,int&"的调用不匹配

t(std::forward(args(…(;

~^~~~~~~

这似乎表明,即使A的实例作为第一个参数传递,重载函数

void(*)(T&&,Args&&...) 

被调用,而不是

void(*)(A&&,T&&,Args&&...) 

使用

template<typename T,typename ... Args>
void run(A&& a,
T&& t,
Args&& ... args)

a不是转发引用,而是右值引用。这意味着当您执行run(a,f,v_function);时,将不会选择该函数,因为a是一个左值,并且这些函数不能绑定到右值引用。有两种快速方法可以解决这个问题。首先,像一样在a上使用std::move

run(std::move(a),f,v_function);

但这不太好。a实际上并没有在函数中移动,所以您有点违反了最小惊喜的原则。

第二个选项是将函数中的A设置为模板类型,使其成为转发引用,然后可以将其约束为类似的A类型

template<typename A_, typename T,typename ... Args, std::enable_if_t<std::is_same_v<std::decay_t<A_>, A>, bool> = true>
void run(A_&& a,
T&& t,
Args&& ... args)
{
a.print();
t(std::forward<Args>(args)...);
}

如果使用rvalue调用run,则代码可以工作。

这里是一个可玩的例子。

正如NathanOliver已经悲伤的那样:空虚的run(A&& a, T&& t, Args&& ... args)期待着一个rvalue reference

rvalue reference的基本思想:将rvalue传递给函数(例如字符串文字(。该值将被复制到函数中。这项工作没有必要。相反,您只是在"移动"对该值的引用,使其由程序的不同部分"拥有"。Move构造函数是理解这个问题的一个很好的起点。