推导出c ++ 11中元组元素的类型

bil*_*ill 7 c++ c++11

我有以下代码.

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 删除的答案表明您可以使用

template <typename... Types>       // vv-- change here
void print_tuple(const std::tuple<Types...>& value)
Run Code Online (Sandbox Code Playgroud)

相反:这允许Types被推导为左值引用、右值引用或非引用,具体取决于 的类型value