使用参数包和通用参考进行过载选择

Overload selection with parameter pack and universal references

我一直在尝试将完美转发与我递归解压的参数包一起使用,这让我意识到我并不真正理解在存在通用参考。

我的困惑是由类似于以下的一些代码引起的:

#include <iostream>
template<class T>
void write(const T& data)
{
  std::cout << "Called write(const T& data)" << std::endl;
}

template<class T, class ...U>
void write(T&& obj, U&&... objs)
{
  std::cout << "Called write(T&& obj, U&&... objs)" << std::endl;
}

int main(int, char**)
{
  int j = 0;
  write(j);
  return 0;
}

当运行时,选择了void write(T&& obj, U&&... objs)重载,但是如果我将void write(const T& data)的签名更改为void write(const T data)void write(T& data)void write(T data) 然后调用该函数。

为什么未选择 void write(const T& data) 重载,但选择了 void write(const T data)void write(T& data)void write(T data)

编辑:我最初认为这个问题可能与 std::forward 的使用有关;然而,它似乎更多是通用参考的结果。我的原始示例如下:

#include <iostream>

void write()
{
  std::cout << "Writing nothing" << std::endl;
}

void write(const char* data)
{
  std::cout << "Writing const char*: " << data << std::endl;
}

template<class T>
void write(const T& data)
{
  std::cout << "Writing generic: " << data << std::endl;
}

template<class T, class ...U>
void write(T&& obj, U&&... objs)
{
  write(std::forward<T>(obj));
  write(std::forward<U>(objs)... );
}

int main(int, char**)
{
  int j = 0;
  write("a", j);
  return 0;
}

这个问题的答案可能有点复杂。我会尽量简单一点。

看起来,在引用合并之后,重载决议的候选者是 write(const T&)write(T&),两者都 T = int,第二个有 U... = (none)。这样后一个被pick出来,转发函数又被调用了

除了之外,对于T类型的左值参数,T&胜过const T&是一个特殊规则。对于其他情况(const TT&T),这两个隐式转换序列都是精确匹配(T -> const T是限定调整,也是精确匹配)因此都没有超过other,第一个write比第二个更专业,所以选择第一个。