请考虑以下以下代码段:
template <class T>
using identity = T;
template <class T>
void foo(identity<T>&&) {}
int main()
{
int i{};
foo(i);
}
Run Code Online (Sandbox Code Playgroud)
i是一个左值,因此如果foo声明一个转发引用参数,它应该编译.但是,如果identity<T>&&转为int&&,则应该引发错误.
该代码在GCC 6.0.0(演示)中编译.
代码无法在Clang 3.7.0(演示)中编译,并显示错误消息:
error: no known conversion from 'int'
to 'identity<int> &&' (aka 'int &&') for 1st argument
Run Code Online (Sandbox Code Playgroud)
哪一个是对的?
想象一下以下简化代码:
#include <iostream>
void foo(const int& x) { do_something_with(x); }
int main() { foo(42); return 0; }
Run Code Online (Sandbox Code Playgroud)
(1)除了优化之外,42传递给什么时会发生什么foo?
编译器是否在某处(在堆栈上?)并将其地址传递给foo?
(1a)标准中是否有任何内容规定在这种情况下要做什么(或者它是否严格取决于编译器)?
现在,想象一下略有不同的代码
#include <iostream>
void foo(const int& x) { do_something_with(x); }
struct bar { static constexpr int baz = 42; };
int main() { foo(bar::baz); return 0; }
Run Code Online (Sandbox Code Playgroud)
它不会链接,除非我定义int bar::baz;(由于ODR?).
(2)除了ODR之外,为什么编译器不能对上面的42做什么呢?
简化事物的一种明显方法是定义foo为:
void foo(int x) { do_something_with(x); }
Run Code Online (Sandbox Code Playgroud)
但是,如果是模板,会怎么做?例如:
template<typename T>
void foo(T&& x) { do_something_with(std::forward<T>(x)); }
Run Code Online (Sandbox Code Playgroud)
(3)是否有一种优雅的方式来foo表示接受x原始类型的价值?或者我是否需要专注于SFINAE或其他一些?
编辑 …
c++ one-definition-rule c++11 pass-by-const-reference forwarding-reference
让我们有一个名为Y的函数重载:
void Y(int& lvalue)
{ cout << "lvalue!" << endl; }
void Y(int&& rvalue)
{ cout << "rvalue!" << endl; }
Run Code Online (Sandbox Code Playgroud)
现在,让我们定义一个像std :: forward一样的模板函数
template<class T>
void f(T&& x)
{
Y( static_cast<T&&>(x) ); // Using static_cast<T&&>(x) like in std::forward
}
Run Code Online (Sandbox Code Playgroud)
现在看看main()
int main()
{
int i = 10;
f(i); // lvalue >> T = int&
f(10); // rvalue >> T = int&&
}
Run Code Online (Sandbox Code Playgroud)
正如预期的那样,输出是
lvalue!
rvalue!
Run Code Online (Sandbox Code Playgroud)
现在回到模板功能f()并替换static_cast<T&&>(x)为static_cast<T>(x).让我们看看输出:
lvalue!
rvalue!
Run Code Online (Sandbox Code Playgroud)
一样的!为什么?如果它们是相同的,那么为什么std::forward<>从返回一个投x来 …
我知道我能做到
auto&& bla = something();
Run Code Online (Sandbox Code Playgroud)
并且根据const返回值的something不同,我会得到一个不同的类型bla.
这是否也适用于结构化绑定案例,例如
auto&& [bla, blabla] = something();
Run Code Online (Sandbox Code Playgroud)
我猜是这样的(结构化绑定捎带在auto初始化器上,表现得像这样),但我找不到肯定的肯定.
更新:初步测试似乎符合我的预期(const正确推导出来):
#include <tuple>
using thing = std::tuple<char, int*, short&, const double, const float&>;
int main()
{
char c = 0;
int i = 1;
short s = 2;
double d = 3.;
float f = 4.f;
thing t{c, &i, s, d, f};
auto&& [cc, ii, ss, dd, ff] = t;
c = 10;
*ii = 11; …Run Code Online (Sandbox Code Playgroud) c++ language-lawyer forwarding-reference c++17 structured-bindings
看一下这两个重载的函数模板:
template <class T>
int foo(T& x) { // #1
return 1;
}
template <class T>
int foo(T&& x) { // #2
return 2;
}
Run Code Online (Sandbox Code Playgroud)
我foo通过以下方式调用:
int i;
foo(i); // calls #1
Run Code Online (Sandbox Code Playgroud)
并且明确选择了重载#1:https ://gcc.godbolt.org/z/zchK1zxMW
这似乎是一个正确的行为,但我无法理解为什么会发生(而且我实际上有代码,这不是我所期望的)。
从过载分辨率:
如果任何候选者是函数模板,则其特化是使用模板参数推导生成的,并且此类特化被视为与非模板函数一样,除非在决胜规则中另有指定。
好的,让我们生成专业化。对于#1来说,这很容易,它就变成了int foo(int& x)。对于 #2,适用特殊扣除规则,因为它是转发参考。i是一个左值,因此T被推导为int&并T&&变为int& &&,在引用折叠后变为 just int&,产生结果int foo(int& x)。这与#1 完全相同!
所以我希望看到一个不明确的调用,但这并没有发生。谁能解释一下为什么吗?这里使用什么决定因素来选择最佳可行的功能?
另请参阅Slack 中的相关讨论,其中有一些想法。
使用转发引用时,将相同的值转发给多个函数是不是一个坏主意?考虑以下代码:
template<typename Container>
constexpr auto
front(Container&& c)
-> typename Container::value_type
{ return std::forward<Container>(c).front(); }
template<typename Container>
constexpr auto
back(Container&& c)
-> typename Container::value_type
{ return std::forward<Container>(c).back(); }
template<typename Container>
constexpr auto
get_corner(Container&& c)
{
return do_something(front(std::forward<Container(c)),
back(std::forward<Container>(c));
}
Run Code Online (Sandbox Code Playgroud)
如果Container是左值引用,则该函数可以正常工作.但是,我担心rvalues传递给它的情况,因为一旦移动操作发生,该值将被无效.我的疑问是:在这种情况下,是否有正确的方法来转发容器,而不会丢失价值类别?
我最近看了一下这个视频,解释了C++中概念精简版的概念,这些概念很可能在今年作为TS出现.现在,我也了解通用引用/转发引用(如描述在这里),并是t &&可以根据上下文两个方面的含义(即,如果类型推演正在执行或没有).这自然会引出概念如何与通用引用相互作用的问题?
为了使它具体化,在下面的例子中我们有
void f(int&& i) {}
int i = 0;
f(i); // error, looks for f(int&)
f(0); // fine, calls f(int&&)
Run Code Online (Sandbox Code Playgroud)
和
template <typename T>
void f(T&& test) {}
int i = 0;
f(i); // fine, calls f(T&&) with T = int& (int& && = int&)
f(0); // fine, calls f(T&&) with T = int&& (int&& && = int&&)
Run Code Online (Sandbox Code Playgroud)
但是如果我们使用概念会发生什么?
template <typename T>
requires Number<T>
void f(T&& test) {}
template <Number T>
void g(T&& test) …Run Code Online (Sandbox Code Playgroud) c++ c++-concepts universal-reference forwarding-reference c++17
我正在尝试制作模板包装函数,该函数应该转发参数并返回值。我无法决定使用什么auto&&或decltype(auto)返回类型更好。我读过 Scott Meyers 的文章,并理解与不剥离 ref_qualifiersdecltype(auto)相比,有必要返回。auto据我了解,同样的论点适用于使用auto&&over auto。现在我有以下问题:
decltype(auto)和何时没有区别?auto&&rvalue,例如:return int{};?返回值会是悬空引用吗?decltype(auto)和 和有什么区别auto&&?什么更适合作为远期回报类型?在模板的上下文中,应用以下"引用折叠"规则:
template <typename T>
void foo(T && t)
{
//T& & -> T&
//T& && -> T&
//T&& & -> T&
//T&& && -> T&&
}
Run Code Online (Sandbox Code Playgroud)
为什么语言禁止使用const限定符的"通用引用" ?
template <typename T>
void foo(T const && t)
Run Code Online (Sandbox Code Playgroud)
如果类型已经解决了参考(4个案例中的3个),那似乎是有意义的.
我确信这个想法与语言的其他一些设计方面不相容,但我看不出全貌.
实现std::move基本上如下:
template<typename T>
typename std::remove_reference<T>::type&&
move(T&& t)
{
return static_cast<typename std::remove_reference<T>::type&&>(t);
}
Run Code Online (Sandbox Code Playgroud)
请注意,参数std::move是通用引用(也称为转发引用,但我们不在此处转发).也就是说,你可以std::move使用左值和左值:
std::string a, b, c;
// ...
foo(std::move(a)); // fine, a is an lvalue
foo(std::move(b + c)); // nonsense, b + c is already an rvalue
Run Code Online (Sandbox Code Playgroud)
但是,由于整个观点std::move都是为了施展到一个rvalue,为什么我们甚至允许std::movervalues呢?如果std::move只接受左值,那会不会更有意义?
template<typename T>
T&&
move(T& t)
{
return static_cast<T&&>(t);
}
Run Code Online (Sandbox Code Playgroud)
然后,无意义的表达式std::move(b + c)将导致编译时错误.
std::move对于初学者来说,上面的实现也会更容易理解,因为代码完全按照它的样子执行:它需要一个左值并返回一个右值.您不必了解通用引用,引用折叠和元函数.
那么为什么std::move设计同时采用左值和左值?
c++ ×10
c++11 ×4
templates ×3
c++14 ×2
c++17 ×2
rvalue ×2
alias ×1
c++-concepts ×1
const ×1
overloading ×1
return-type ×1
static-cast ×1