微信公众号搜"智元新知"关注
微信扫一扫可直接关注哦!

c – 由于对模板类型的通用(向前)引用,未能实例化功能模板

Universal references(即“转发参考”,c标准名称)和c11,c14及以上的完美转发具有许多重要优点;见 herehere.

在Scott Meyers上文提到的文章(link)中,有经验说明:

If a variable or parameter is declared to have type T&& for some deduced type T,that variable or parameter is a universal reference.

实施例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):

test.cpp:23:11: error: no matching function for call to ‘f’

auto r1 = f (x1);

test.cpp:5:16: note: candidate function [with T = foo,A = int] not
viable: no kNown conversion from ‘struct foo< int >’ to ‘foo< int > &&’
for 1st argument

decltype(auto) f (T< A > && t)

#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&
> T& &安培;&安培;成为T&
> T&& &安培;成为T&
> T&& &安培;&安培;成为T&&

由于第二条规则,f的第一个参数的“有效”类型是一个左值引用,因此您可以将其值绑定到它.

现在当你定义一个函数g like …

template<template<class> class T,typename A>
auto g(T<A>&&);

那么无论什么,模板参数的扣除都必须把T变成一个模板,而不是一个类型.毕竟,你明确指出了当模板参数作为模板< class>类而不是typename.
(这是一个重要的区别,在你的例子中,foo不是一个类型,它是一个模板,你可以看到类型级别的功能,但是回到主题)

现在,T是某种模板.您不能引用模板.
引用(类型)是从(可能不完整的)类型构建的.所以无论什么,T A (它是类型,但不是可以推导出的模板参数)不会变成(左值)引用,这意味着T< A> &安培;&安培;不需要任何崩溃并保留它是什么:一个r值的参考.当然,您不能将左值绑定到rvalue引用.

但是,如果你通过一个价值,那么甚至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)

[…] can you explain why it is not an universal reference? what would the danger or the pitfall of it be,or is it too difficult to implement? I am sincerely interested.

T&LT a取代;&安培;&安培;不是通用参考,因为T< A>不是模板参数.它是(扣除T和A之后)简单(固定/非通用)类型.

将此作为转发参考的一个严重缺陷将是您不能再表达T&A&&&

原文地址:https://www.jb51.cc/c/114533.html

版权声明:本文内容由互联网用户自发贡献,该文观点与技术仅代表作者本人。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌侵权/违法违规的内容, 请发送邮件至 dio@foxmail.com 举报,一经查实,本站将立刻删除。

相关推荐