c – 由于对模板类型的通用(向前)引用,未能实例化功能模板
Universal references(即“转发参考”,c标准名称)和c11,c14及以上的完美转发具有许多重要优点;见
here和
here.
在Scott Meyers上文提到的文章(link)中,有经验说明:
实施例1 实际上,使用clang,我们看到以下代码片段将使用-std = c 14成功编译: #include <utility> template <typename T> decltype(auto) f(T && t) { return std::forward<T>(t); } int x1 = 1; int const x2 = 1; int& x3 = x1; int const& x4 = x2; // all calls to `f` result in a successful // binding of T&& to the required types auto r1 = f (x1); // various lvalues okay,as expected auto r2 = f (x2); // ... auto r3 = f (x3); auto r4 = f (x4); auto r5 = f (int()); // rvalues okay,as expected 给出任何关于通用引用(转发参考)和类型扣除的描述(参见例如this explanation),这是很清楚的,为什么上述工作.虽然从同样的解释来看,为什么下面的工作也不尽如人意, (失败)示例2 This question解决了同样的问题.然而,提供的答案不能解释为什么模板类型不被归类为“推导”. 我将要展示的(貌似)满足Meyers上述的要求.但是,以下代码剪切无法编译,产生错误(其中每个调用f):
#include <utility> // // It **seems** that the templated type T<A> should // behave the same as an bare type T with respect to // universal references,but this is not the case. // template <template <typename> typename T,typename A> decltype(auto) f (T<A> && t) { return std::forward<T<A>> (t); } template <typename A> struct foo { A bar; }; struct foo<int> x1 { .bar = 1 }; struct foo<int> const x2 { .bar = 1 }; struct foo<int> & x3 = x1; struct foo<int> const& x4 = x2; // all calls to `f` **fail** to compile due // to **unsuccessful** binding of T&& to the required types auto r1 = f (x1); auto r2 = f (x2); auto r3 = f (x3); auto r4 = f (x4); auto r5 = f (foo<int> {1}); // only rvalue works 在上下文中,由于类型T A推论出f的参数,确定参数声明T A&& t将作为通用参考(正向参考). 示例3(为了清楚描述现有问题) 让我强调以下内容:示例2中的代码的失败不是由于struct foo<>是模板类型.失败似乎只是通过将f的参数声明为模板类型. 考虑以下代码的修改,现在编译: #include <utility> // // If we re-declare `f` as before,where `T` is no longer a // templated type parameter,our code works once more. // template <typename T> decltype(auto) f (T && t) { return std::forward<T> (t); } // // Notice,`struct foo<>` is **still** a templated type. // template <typename A> struct foo { A bar; }; struct foo<int> x1 { .bar = 1 }; struct foo<int> const x2 { .bar = 1 }; struct foo<int> & x3 = x1; struct foo<int> const& x4 = x2; // all calls to `f` (again) result in // a successful binding of T&& to the required types auto r1 = f (x1); auto r2 = f (x2); auto r3 = f (x3); auto r4 = f (x4); 令我惊讶的是,这个简单的改变完全改变了模板函数f的类型参数的类型扣除的行为. 问题: 为什么第二个例子不按预期工作?有没有技术来克服这个问题与模板类型在c 11/14?有没有众所周知的,现有的代码库(在野外)成功地使用c的向前引用模板类型? 解决方法
当你调用一些函数f一些左值时:
int a = 42; f(a); 那么f必须能够接受这样的左值.当f的第一个参数是(lvalue)引用类型时,或者根本不是引用时,情况就是这样: auto f(int &); auto f(int); // assuming a working copy constructor 当参数是r值引用时,这将不起作用: auto f(int &&); // error 现在,当您将第一和第三个示例中的转发参考定义为第一个参数时 template<typename T> auto f(T&&); // Showing only declaration …并且您实际上使用lvalue调用此函数,模板类型扣除将T转换为(lvalue)引用(这可能在我稍后提供的示例代码中看到): auto f(int & &&); // Think of it like that 当然,上面有太多的参考.所以C有collapsing rules,其实很简单: > T& &安培;成为T& 由于第二条规则,f的第一个参数的“有效”类型是一个左值引用,因此您可以将其值绑定到它. 现在当你定义一个函数g like … template<template<class> class T,typename A> auto g(T<A>&&); 那么无论什么,模板参数的扣除都必须把T变成一个模板,而不是一个类型.毕竟,你明确指出了当模板参数作为模板< class>类而不是typename. 现在,T是某种模板.您不能引用模板. 但是,如果你通过一个价值,那么甚至g将工作. 以上所有可以在以下示例中看到: template<typename X> struct thing { }; template<typename T> decltype (auto) f(T&& t) { if (std::is_same<typename std::remove_reference<T>::type,T>::value) { cout << "not "; } cout << "a reference" << endl; return std::forward<T>(t); } template< template<class> class T,typename A> decltype (auto) g(T<A>&& t) { return std::forward<T<A>>(t); } int main(int,char**) { thing<int> it {}; f(thing<int> {}); // "not a reference" f(it); // "a reference" // T = thing<int> & // T&& = thing<int>& && = thing<int>& g(thing<int> {}); // works //g(it); // T = thing // A = int // T<A>&& = thing<int>&& return 0; } (Live here) 关于如何“克服”这个:你不能.至少不是你似乎想要的方式,因为自然解决方案是你提供的第三个例子:既然你不知道传递的类型(这是一个左值引用,一个rvalue引用还是一个引用)?必须保持它像T一样的通用.你可以提供超负荷,但是这样会失败的是完美转发的目的,我猜. 嗯,原来你实际上可以克服这个,使用一些traits类: template<typename> struct traits {}; template< template<class>class T,typename A> struct traits<T<A>> { using param = A; template<typename X> using templ = T<X>; }; 然后,您可以在函数内部提取模板和实例化模板的类型: template<typename Y> decltype (auto) g(Y&& t) { // Needs some manual work,but well ... using trait = traits<typename std::remove_reference<Y>::type>; using A = typename trait::param; using T = trait::template templ // using it T<A> copy{t}; A data; return std::forward<Y>(t); } (Live here)
T< a取代;&安培;&安培;不是通用参考,因为T< A>不是模板参数.它是(扣除T和A之后)简单(固定/非通用)类型. 将此作为转发参考的一个严重缺陷将是您不能再表达T&A&&& (编辑:李大同) 【声明】本站内容均来自网络,其相关言论仅代表作者个人观点,不代表本站立场。若无意侵犯到您的权利,请及时与联系站长删除相关内容! |