为什么需要std::reference_wrapper?应该在哪里使用?它与简单的指针有什么不同?它的性能与简单的指针相比如何?
假设我有一些类型的对象T,我想把它放到一个引用包装器中:
int a = 5, b = 7;
std::reference_wrapper<int> p(a), q(b); // or "auto p = std::ref(a)"
Run Code Online (Sandbox Code Playgroud)
现在我可以很容易地说if (p < q),因为引用包装器已转换为其包装类型.一切都很开心,我可以处理一组参考包装器,就像它们是原始对象一样.
(正如下面链接的问题所示,这可以是生成现有集合的备用视图的有用方法,可以随意重新排列,而不会产生完整副本的成本,以及维护原始集合的更新完整性. )
但是,对于某些类,这不起作用:
std::string s1 = "hello", s2 = "world";
std::reference_wrapper<std::string> t1(s1), t2(s2);
return t1 < t2; // ERROR
Run Code Online (Sandbox Code Playgroud)
我的解决方法是在这个答案中定义一个谓词*; 但我的问题是:
为什么以及何时可以将运算符应用于引用包装器并透明地使用包装类型的运算符?为什么会失败std::string?它与std::string模板实例的事实有什么关系?
*)更新:根据答案,似乎使用std::less<T>()是一般解决方案.
c++ templates implicit-conversion reference-wrapper template-argument-deduction
使用以下代码可以清楚地解决该问题:
#include <functional>
#include <iostream>
#include <vector>
int main() {
//std::vector<int> a, b;
int a = 0, b = 0;
auto refa = std::ref(a);
auto refb = std::ref(b);
std::cout << (refa < refb) << '\n';
return 0;
}
Run Code Online (Sandbox Code Playgroud)
如果我使用注释std::vector<int> a, b;而不是int a = 0, b = 0;,则代码不会在GCC 5.1,clang 3.6或MSVC'13中的任何一个上编译.在我看来,std::reference_wrapper<std::vector<int>>可以隐式转换std::vector<int>&为LessThanComparable,因此它应该是LessThanComparable本身.有人可以向我解释一下吗?
c++ language-lawyer implicit-conversion c++11 reference-wrapper
通常,rvalues可以绑定到const引用(const SomeType&).它内置于语言中.但是,std::reference_wrapper<const T>不接受rvalue作为其构造函数参数,因为故意删除了相应的重载.这种不一致的原因是什么?std::reference_wrapper当我们必须传递值但希望保留引用语义时,"广告"作为引用变量的替代.
换句话说,如果const &绑定的右值被认为是安全的,因为它内置于语言中,为什么C++ 11的设计者不允许包含rvalues std::reference_wrapper<const T>?
什么时候会派上用场,你可能会问.例如:
class MyType{};
class Foo {
public:
Foo(const MyType& param){}
};
class MultiFoo {
public:
MultiFoo(std::initializer_list<std::reference_wrapper<const MyType>> params){}
};
int main()
{
Foo foo{MyType{}}; //ok
MultiFoo multiFoo{MyType{}, MyType{}}; //error
}
Run Code Online (Sandbox Code Playgroud) 这个问题的灵感来自于std :: reference_wrapper的问题.让我们说,例如,operator<对于std::vector.它被定义为函数模板
template< class T, class Alloc >
bool operator<( const vector<T,Alloc>& lhs,
const vector<T,Alloc>& rhs );
Run Code Online (Sandbox Code Playgroud)
因此,拒绝将函数参数隐式转换为相应函数参数的类型(主要是因为其模板性质).这大大降低了它的实用性和便利性std::reference_wrapper.例如,您不能使用std::sort的std::vector<std::reference_wrapper<std::vector<int>>>.
另一方面,只有当operator<定义为非模板Koenig运算符时,才能解决所有问题
template <...>
class vector ... {
friend bool operator<(const vector& a, const vector& b) {...}
};
Run Code Online (Sandbox Code Playgroud)
我想知道为什么标准库采用了前一种方法而不是这种方法?
c++ standard-library implicit-conversion c++11 reference-wrapper
在C++ 11标准中,它指出(参见cppreference.com,另见标准的第20.4.2.4节)它指出
template< class... Types >
tuple<VTypes...> make_tuple( Types&&... args );
Run Code Online (Sandbox Code Playgroud)
创建一个元组对象,从参数类型中推导出目标类型.
对于每一个
Ti在Types...时,相应的类型Vi中Vtypes...是std::decay<Ti>::type除非的应用std::decay在结果中std::reference_wrapper<X>为某种类型的X,在这种情况下,推定的类型是X&.
我想知道:为什么参考包装器在这里处理特殊?
STL使用"小于"作为默认比较器.即使基础类定义了"<"运算符,对使用reference_wrapper <>包装的对象的STL比较器调用也不会编译.
看来,这是因为有没有隐式转换上所进行的上LHS所述的LHS.operator <(右轴),当它是一个成员函数.我已经确认使用免费版本作为比较器工作.
但是,如果reference_wrapper提供了"<"运算符,它在底层调用"<",则需要使用free函数.
我在reference_wrapper的代码中添加了以下内容(取自VS11 Beta xrefwrap.h),并且可以使用std :: map和我的reference_wrapper <>版本中包含的类,其中定义了"<"运算符.
bool operator <(reference_wrapper<_Ty> const rhs) const {
return this->get() < rhs.get();
}
Run Code Online (Sandbox Code Playgroud)
稍后添加:如果我理解正确,reference_wrapper <>提供与许多库所需的ptrs相关联的复制/分配语义,同时隐藏与ptr相关的语法.这允许使用引用类型语法,而不需要本地副本的开销.要将它与使用ptrs的示例进行比较,完全错过了reference_wrappers的一个要点:您希望避免使用ptr类型语法.
事情就是这样,当对象被包装在reference_wrappers中时,直接处理对象的代码会中断.不用说,"<"是默认的比较器,确实让它变得特别; 在很大比例的现有代码中,对象将定义这些以避免对特殊比较器的需求.
后来添加#2:此功能的历史表明,避免使用ptr语法不是最初的意图.然而,自推出首次推出以来已经过去了十年.由于大量新程序员"被引导" 以避免基于ptr的语法(毫无疑问受ptr免费语言的影响),如果它可以更加无缝地工作,特别是在处理在STL容器中存储对象的遗留代码时,该功能会变得越来越有用,并且价值全面复制.
稍后添加#3:使用最少的代码更改改进旧版代码 随着时间的推移,瘦类变得很重,容器中对象的大小也会增加.提高性能的一种快速方法是通过包装对象来避免副本.这将提供"C ptr"类型的性能,而无需额外的副本,只需对代码进行最小的更改.
std::map<const Object, string> objTable;
// can be rewritten as to avoid object copies using the
// __myOwn::reference_wrapper which contains the '<' operator
std::map<__myOwn::reference_wrapper<const Object>, string> rwTable_myOwn;
// which works with out any non-member free …Run Code Online (Sandbox Code Playgroud) 我想要一个排序的视图,std::vector<std::chrono::milliseconds>但我不想修改原始容器.std::reference_wrapper这似乎是完美的,它适用于整数向量.
我创建了这个小例子:
#include <chrono>
#include <vector>
#include <iostream>
#include <algorithm>
#include <functional>
int main()
{
std::vector<int> numbers{1, 42, 3, 9, 5};
std::vector<std::reference_wrapper<int>> sorted_numbers(numbers.begin(), numbers.end());
std::sort(sorted_numbers.begin(), sorted_numbers.end());
std::cout << "Numbers: ";
for (const auto& n : numbers)
std::cout << n << ' ';
std::cout << '\n';
std::cout << "Sorted numbers: ";
for (const auto& n : sorted_numbers)
std::cout << n << ' ';
std::cout << '\n';
std::cout << "Numbers: ";
for (const auto& n : numbers)
std::cout …Run Code Online (Sandbox Code Playgroud) 我有一个类,复制构造函数和构造函数采用std::reference_wrapper:
#include <functional>
#include <iostream>
class Class {
public:
Class() {
std::cout << "Class()" << std::endl;
}
Class(Class const &) {
std::cout << "Class(Class const &)" << std::endl;
}
Class(std::reference_wrapper<Class>) {
std::cout << "Class(std::reference_wrapper<Class>)" << std::endl;
}
Class(std::reference_wrapper<const Class>) {
std::cout << "Class(std::reference_wrapper<const Class>)" << std::endl;
}
};
int main() {
Class a;
Class b = a;
Class c = std::ref(a);
Class d = std::cref(a);
}
Run Code Online (Sandbox Code Playgroud)
当正常编译时(g++ --std=c++17 test.cpp),它按照需要工作,按顺序调用四个构造函数:
$ ./a.exe
Class()
Class(Class const &)
Class(std::reference_wrapper<Class>) …Run Code Online (Sandbox Code Playgroud) 我一直试图从这里std::reference_wrapper理解 的实现,如下:
namespace detail {
template <class T> constexpr T& FUN(T& t) noexcept { return t; }
template <class T> void FUN(T&&) = delete;
}
template <class T>
class reference_wrapper {
public:
// types
typedef T type;
// construct/copy/destroy
template <class U, class = decltype(
detail::FUN<T>(std::declval<U>()),
std::enable_if_t<!std::is_same_v<reference_wrapper, std::remove_cvref_t<U>>>()
)>
constexpr reference_wrapper(U&& u) noexcept(noexcept(detail::FUN<T>(std::forward<U>(u))))
: _ptr(std::addressof(detail::FUN<T>(std::forward<U>(u)))) {}
reference_wrapper(const reference_wrapper&) noexcept = default;
// assignment
reference_wrapper& operator=(const reference_wrapper& x) noexcept = default;
// access
constexpr operator T& () const …Run Code Online (Sandbox Code Playgroud)