- android - RelativeLayout 背景可绘制重叠内容
- android - 如何链接 cpufeatures lib 以获取 native android 库?
- java - OnItemClickListener 不起作用,但 OnLongItemClickListener 在自定义 ListView 中起作用
- java - Android 文件转字符串
Universal references (即“前向引用”,c++
标准名称)和 c++11
中的完美转发, c++14
, beyond 有很多重要的优势;见here , 和 here .
在上面引用的 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.
确实,使用 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 )的任何描述,很明显为什么上面的方法有效。虽然,从同样的解释来看,为什么下面的方法也不起作用还不是很清楚。
This question解决了同样的问题。然而,所提供的答案并没有解释为什么模板化类型没有被归类为“推导”。
我要展示的(表面上)满足迈耶斯的上述要求。但是,以下代码截断了编译失败,产生了错误(以及对 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
将充当通用引用(前向引用)。
让我强调以下几点:Example 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
的第一个参数就是这种情况是(左值)引用类型,或者当它根本不是引用时:
auto f(int &);
auto f(int); // assuming a working copy constructor
当参数是右值引用时,这不会工作:
auto f(int &&); // error
现在,当您像在第一个和第三个示例中所做的那样定义一个将转发引用作为第一个参数的函数时...
template<typename T>
auto f(T&&); // Showing only declaration
...你实际上用一个左值调用这个函数,模板类型推导变成了T
进入一个(左值)引用(这种情况可以在我稍后提供的示例代码中看到):
auto f(int & &&); // Think of it like that
当然,上面涉及的引用太多了。所以 C++ 有 collapsing rules ,其实很简单:
T& &
变成 T&
T& &&
变成 T&
T&& &
变成 T&
T&& &&
变成 T&&
感谢第二条规则,f
的第一个参数的“有效”类型是左值引用,因此您可以将左值绑定(bind)到它。
现在当你定义一个函数时 g
喜欢...
template<template<class> class T, typename A>
auto g(T<A>&&);
那么无论如何,模板参数推导必须转T
到一个模板,不是一个类型。毕竟,在将模板参数声明为 template<class> class
时,您已准确指定了它而不是 typename
.(这是一个重要的区别,foo
在您的示例中不是类型,它是一个模板......您可以将其视为类型级别的函数,但回到主题)
现在,T
是某种模板。您不能引用模板。引用(类型)是从(可能不完整的)类型 构建的。所以无论如何,T<A>
(这是一种类型,但不是可以推导的模板参数)不会变成(左值)引用,这意味着 T<A> &&
不需要任何折叠并保持原样:右值引用。当然,您不能将左值绑定(bind)到右值引用。
但是如果你给它传递一个右值,那么即使是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 )
关于如何“克服”这一点:你不能。至少不是你想要的方式,因为自然的解决方案是你提供的第三个例子:因为你不知道传递的类型(它是左值引用,右值引用还是在全部?)你必须保持它像T
一样通用.您当然可以提供过载,但我猜这会以某种方式破坏完美转发的目的。
嗯,事实证明你实际上可以克服这个问题,使用一些特征类:
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<A>&&
不是通用引用,因为 T<A>
不是模板参数。它是(在扣除 T
和 A
之后)一个简单的(固定/非通用)类型。
将其设为转发引用的一个严重缺陷是您无法再表达 T<A>&&
的当前含义。 : 对从模板构建的某种类型的右值引用 T
带参数 A
.
关于c++ - 由于对模板化类型的通用(前向)引用而无法实例化函数模板,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/32282705/
前一段时间写过一篇文章《 实战,一个高扩展、可视化低代码前端,详实、完整 》,得到了很多朋友的关注。 其中的逻辑编排部分过于简略,不少朋友希望能写一些关于逻辑编排的内容,本文就详细讲述一下逻辑
我正在尝试以下 Java 片段: int[] testArray={10,20,30,40}; int i= 0; testArray[i++]= testArray[i++]+1; System.o
我想知道我是否可以通过某种方式在 C++ 中进行前/后函数调用。我有一个包含很多函数的包装器类,在每次调用包装器函数后,我应该调用另一个始终相同的函数。 所以我不想像这样对每个函数调用 postFun
我有一个像这样的头文件: #pragma once #include "gamestate.h" #include "ExitListener.h" class InitialGameState :
学习左值和右值。定义是任何可以是“地址”的东西都是左值,否则就是右值。 我检查了运算符的优先级,前缀和后缀增量都比“地址”运算符具有更高的优先级。 对于下面的两个例子,谁能解释一下为什么第一个“&++
在我的学习过程中,我遇到了前后迭代器,我想知道是否有办法让它们就地创建容器元素。从文档来看,容器似乎需要实现 push_back 函数才能与 back_iterator 一起使用。但是有没有一种方法可
我有两个关于 Java 中运算符优先级的类似问题。 第一个: int X = 10; System.out.println(X++ * ++X * X++); //it prints 1440 根据
请放轻松,不要对我开枪,因为我还是新手。 当我运行这段代码时,我完全糊涂了,终生无法弄清楚为什么: int y = 9; cout << "++y = " << ++y << "\n--y = " <
两种表达方式有区别吗: (*x)++ 和 ++(*x) 我可以看到这两个语句都替换了 *x 中 (*x+1) 的内容。但是它们之间有什么区别吗? 最佳答案 (*x)++ 计算为*x的值;作为副作用,*
我有一个如下所示的数据集: Date CONSUMER DISCR CONSUMER STAPLES ENERGY FINANCIALS HEALTH CARE
我希望检查名称字段中输入的前两个字符是否为字母 - 除此之外没有什么区别(空格、'、- 等都是公平的游戏)。这是我到目前为止所拥有的,但它不起作用。想法?谢谢! if (document.form01
我制作了一个简单的脚本,为像素和所有附近的像素着色为相同的颜色 Click foto
我需要编写一个循环,以下列格式输出从昨天算起的最近 30 天: 2014-02-02 2014-02-03 2014-02-04 ... 2014-03-04 我想我需要像这样使用循环: for ($
我正在做一些练习,但我对这个感到困惑: public static int f (int x, int y) { int b=y--; while (b>0) { if (x%2!=0
我需要一个 4 个字符的正则表达式。前 3 个字符必须是数字,最后 1 个字符必须是字母或数字。 我形成了这个,但它不起作用 ^([0-9]{3}+(([a-zA-Z]*)|([0-9]*)))?$
我需要编写一个循环,以下列格式输出从昨天算起的最近 30 天: 2014-02-02 2014-02-03 2014-02-04 ... 2014-03-04 我想我需要像这样使用循环: for ($
我有下面的程序,我试图找到前 1000 个素数的总和。在代码中,解决方案1和2有什么区别?为什么我不应该将 count 变量放在 if 条件之外?如果我把变量放在 if 之外,我显然没有得到我需要的答
这个问题在这里已经有了答案: Replace First N Occurrences in the String (7 个答案) 关闭 4 年前。 我有一个如下的字符串 const str = '_
我正在尝试测量以纳秒为单位的平均访问延迟,但在第一次迭代后我收到“段错误(核心转储)”。我错过了什么吗?我是否滥用了指针。这是导致错误的函数: #include #include #include
我有一个 SQL 问题 (MySQL)。我如何从下表创建一个新表(表名称:“well_master_prod_inj”)。 我需要按井名和日期聚合数据。我希望每个井名只有一行数据以及显示以下数据的列:
我是一名优秀的程序员,十分优秀!