考虑一个X
具有N
成员变量的类,每个类都有一些可复制和可移动的类型,以及N
相应的setter函数.在C++ 98中,定义X
可能如下所示:
class X
{
public:
void set_a(A const& a) { _a = a; }
void set_b(B const& b) { _b = b; }
...
private:
A _a;
B _b;
...
};
Run Code Online (Sandbox Code Playgroud)
X
上面类的setter函数可以绑定到左值和右值参数.根据实际的说法,这可能导致创建临时的,并会最终导致的分配副本; 因此,此设计不支持不可复制的类型.
使用C++ 11,我们可以移动语义,完美转发和通用引用(Scott Meyers的术语),通过以下方式重写它们,可以更有效和广泛地使用setter函数:
class X
{
public:
template<typename T>
void set_a(T&& a) { _a = std::forward<T>(a); }
template<typename T>
void set_b(T&& b) { _b = std::forward<T>(b); } …
Run Code Online (Sandbox Code Playgroud) c++ move-semantics perfect-forwarding c++11 universal-reference
此函数的参数将绑定到右值引用:
void f(int && i);
Run Code Online (Sandbox Code Playgroud)
但是,此函数的参数将绑定到右值或左值引用:
template <typename T>
void f(T && t);
Run Code Online (Sandbox Code Playgroud)
我经常听到这被称为通用参考.
我也听说它被称为转发参考.
他们的意思是一样的吗?
如果函数体调用,它只是转发引用std::forward
吗?
c++ templates perfect-forwarding universal-reference forwarding-reference
假设我使用基于范围的循环编程时的当前规则说
使用
for(auto const &e :...)
或for(auto &e:...)
在可能时使用for(auto a: ...)
.
我以我自己的经验和这个问题为例.
但是在读完关于循环的新简洁之后我想知道,我不应该用我&
的规则替换我的规则&&
吗?正如这里所写,这看起来像迈耶斯的通用参考.
所以,我问自己,如果我的新规则要么
使用
for(auto const &&e :...)
或for(auto &&e:...)
在可能的时候......
或者这不总是有效,因此应该是相当复杂的
检查
for(auto const &&e :...)
或者for(auto &&e:...)
是可能的,再考虑for(auto const &e :...)
或者for(auto &e:...)
,并且只在需要时不使用引用.
我知道如果声明变量或参数具有T&&
某种推导类型的类型T
,那么该变量或参数通常被称为通用引用.
Scott Meyers在其最初的演讲"C++ 11中的通用参考"中引入了 通用参考这一术语.但是,我想知道普遍参考的官方/标准术语是什么.
auto&& mytup = std::make_tuple(9,1,"hello");
std::get<0>(mytup) = 42;
cout << std::get<0>(mytup) << endl;
Run Code Online (Sandbox Code Playgroud)
auto&& var =
func()
总是使用而不是auto var = func()
没有复制/移动?假设我有这个功能:
bool f(int&& one, int&& two) { }
Run Code Online (Sandbox Code Playgroud)
如果我尝试使用此代码调用它:
int x = 4;
f(x, 5);
Run Code Online (Sandbox Code Playgroud)
编译器会抱怨它不能将x从左值引用转换为右值引用,这是正确的.
现在,如果我将f转换为模板函数,如下所示:
template <class T, class U>
bool f(T&& one, U&& two) { }
Run Code Online (Sandbox Code Playgroud)
然后我可以用左值引用来调用它:
int x = 5;
f(x, 5);
Run Code Online (Sandbox Code Playgroud)
为什么会这样?为什么编译器在这种情况下不抱怨?
在使用通用引用的同时,我遇到了clang和gcc在重载决策上不一致的情况.
#include <iostream>
struct foo {};
template<typename T>
void bar(T&) { std::cout << "void bar(T&)\n"; }
template<typename T>
void bar(T&&) { std::cout << "void bar(T&&)\n"; }
int main()
{
foo f;
bar(f); // ambiguous on gcc, ok on clang
}
Run Code Online (Sandbox Code Playgroud)
gcc报告上面的调用是模棱两可的.但是,clang选择T&
重载并成功编译.
哪个编译器有问题,为什么?
编辑:
在VS2013预览中测试相同的代码,它同意clang; 除了Intellisense,这是在gcc的一方:-)
c++ overload-resolution c++11 universal-reference forwarding-reference
阿基于范围的for
声明在§6.5.4定义为等同于:
{
auto && __range = range-init;
for ( auto __begin = begin-expr,
__end = end-expr;
__begin != __end;
++__begin ) {
for-range-declaration = *__begin;
statement
}
}
Run Code Online (Sandbox Code Playgroud)
其中range-init
定义了两种基于范围的形式for
:
for ( for-range-declaration : expression ) => ( expression )
for ( for-range-declaration : braced-init-list ) => braced-init-list
Run Code Online (Sandbox Code Playgroud)
(该条款进一步规定了其他子表达式的含义)
为什么__range
给出推导类型auto&&
?我的理解auto&&
是,通过传递它来保留表达式的原始值(左值/右值)是有用的std::forward
.但是,__range
不会通过任何地方std::forward
.它得到的范围内时迭代器作为一个人的只用__range
,__range.begin()
或begin(__range)
.
使用"通用参考"有auto&&
什么好处?还auto&
不够吗? …
当我考虑以下两个重载时:
template <class... T> void f(const T&... x);
template <class T> void f(const T& x);
Run Code Online (Sandbox Code Playgroud)
我有保证f(x)
总是会调用第二个函数,并且永远不会导致歧义.从某种意义上说,无论其类型如何,第二个版本与一个参数的第一个版本相比具有普遍的优先级.
现在考虑一下通用引用和函数的const引用版本的情况:
template <class T> void f(T&& x);
template <class T> void f(const T& x);
Run Code Online (Sandbox Code Playgroud)
我的问题是:它们是这两个函数之间的普遍优先级,不管x的类型(r值引用,引用,cv限定符,指针......),如前一种情况?(如果是的话,优先级是多少?)
让我们假设一个函数func
接受表单中的任何容器Container<Type, N, Args...>
(这是一个容器,它将第一个模板参数作为一个类型,第二个std::size_t
函数定义容器中有多少个参数)并返回其i
第th个元素,当且仅当N
它在40
和之间时42
.
这种容器的一个例子是std::array
.
我的第一个版本的功能将是:
template
< template<class, std::size_t, class...> class Container
, class Type
, std::size_t N
, class... Args >
auto func(std::size_t i, Container<Type, N, Args...>& container) -> decltype(container[0]) {
static_assert(N >= 40 && N <= 42, "bla bla bla");
return container[i];
}
Run Code Online (Sandbox Code Playgroud)
然后我需要一个const
重载:
template
< template<class, std::size_t, class...> class Container
, class Type
, std::size_t N
, class... …
Run Code Online (Sandbox Code Playgroud)