我有以下代码.
template <typename... Types>
void print_tuple(const std::tuple<Types&&...>& value)
{
std::cout << std::get<0>(value) << "," << std::get<1>(value) << std::endl;
}
print_tuple(std::forward_as_tuple("test",1));
Run Code Online (Sandbox Code Playgroud)
哪个编译器抱怨
error: invalid initialization of reference of type ‘const std::tuple<const char (&&)[5], int&&>&’ from expression of type ‘std::tuple<const char (&)[5], int&&>’
print_tuple(std::forward_as_tuple("test",1));
Run Code Online (Sandbox Code Playgroud)
为什么编译器会将元组中第一个元素的类型推断为const char(&&)[5]?
小智 2
一般来说,为了成功演绎,参数需要具有与参数相同的一般形式。T &&可以推断出一些例外情况U &(通过选择T = U &),但在本例中没有指定此类例外情况。
14.8.2.5 从类型 [temp.deduct.type] 推导模板参数
8可以推导出模板类型实参
T、模板模板实参TT或模板非类型实参,如果且iPA、[...]
T&
T&&[...]
尚不完全清楚,但这要求P(参数)和A(参数)都具有相同的形式。它们需要都是 形式T&,或者都是 形式T&&。T &&在有限的情况下,可以从 推断出例外情况,可以通过在匹配发生之前更改为 plain 来U &完成:T &&T
10 类似地,如果
P具有包含 的形式(T),则将 的相应参数类型列表的每个参数类型与 的相应参数类型列表的相应参数类型进行比较。如果和是在获取函数模板的地址 (14.8.2.2) 或从函数声明 (14.8.2.6) 推导模板参数时源自推导的函数类型,并且 和是 顶级参数类型列表的参数如果of和是对 cv 不合格模板参数的右值引用并且是左值引用,则分别调整 of 和 ,在这种情况下, 的类型更改为模板参数类型(即更改为简单的)。[...]PiPAiAPAPiAiPAPiAiPiT&&T
和
14.8.2.1 从函数调用中推导模板参数 [temp.deduct.call]
3 [...] 如果
P是对 cv 未限定模板参数的右值引用,并且参数是左值,A则使用类型“左值引用”代替A类型推导。[...]
但没有类似的例外适用于您的场景。
正是这个相同的原理使得
template <typename T> struct S { };
template <typename T> void f(S<const T>) { }
int main() { f(S<void()>()); }
Run Code Online (Sandbox Code Playgroud)
invalid:const T无法从 推断出void(),即使T = void()会给出确切的结果,并且调用f<void()>会成功。
Wintermute 删除的答案表明您可以使用
Run Code Online (Sandbox Code Playgroud)template <typename... Types> // vv-- change here void print_tuple(const std::tuple<Types...>& value)
相反:这允许Types被推导为左值引用、右值引用或非引用,具体取决于 的类型value。