Utility components
This header contains utilities in unrelated domains:
- Pairs: objects that can hold two values of different types: pair, make_pair, piecewise_construct, piecewise_construct_t.
- Generic relational operators: Standard definitions for the relational operators
!=
,>
,<=
and>=
under a specific namespace: rel_ops. - Rvalue casts (C++11): Allow the generation of rvalue references: forward, move, move_if_noexcept, declval.
- Generic swap function: definition used by default by the components of the standard library for all types that do not provide their own overload: swap.
swap
non-array (1) | template <class T> void swap (T& a, T& b) noexcept (is_nothrow_move_constructible<T>::value && is_nothrow_move_assignable<T>::value); |
---|---|
array (2) | template <class T, size_t N> void swap(T (&a)[N], T (&b)[N]) noexcept (noexcept(swap(*a,*b))); |
Exchange values of two objects
Exchanges the values of a and b.
template <class T> void swap (T& a, T& b)
{
T c(std::move(a)); a=std::move(b); b=std::move(c);
}
template <class T, size_t N> void swap (T &a[N], T &b[N])
{
for (size_t i = 0; i<N; ++i) swap (a[i],b[i]);
}
标准库的许多组件(在std中)以非限定的方式调用swap,以允许调用非基本类型的自定义重载,而不是此通用版本:在与提供它们的类型相同的命名空间中声明的swap的自定义重载是通过在该通用版本上的参数相关查找来选择的。
// swap algorithm example (C++11)
#include <iostream> // std::cout
#include <utility> // std::swap
int main () {
int x=10, y=20; // x:10 y:20
std::swap(x,y); // x:20 y:10
int foo[4]; // foo: ? ? ? ?
int bar[] = {10,20,30,40}; // foo: ? ? ? ? bar: 10 20 30 40
std::swap(foo,bar); // foo: 10 20 30 40 bar: ? ? ? ?
std::cout << "foo contains:";
for (int i: foo) std::cout << ' ' << i;
std::cout << '\n';
return 0;
}
Output:
foo contains: 10 20 30 40 |
make_pair
template <class T1, class T2> pair<V1,V2> make_pair (T1&& x, T2&& y); // see below for definition of V1 and V2
Construct pair object
构造一个pair对象,其第一个元素设置为x,第二个元素设为y。
模板类型可以从传递给make_pair的参数中隐式推导出来。
pair对象可以由包含不同类型的其他pair对象构造,前提是相应的类型可以隐式转换。
// make_pair example
#include <utility> // std::pair
#include <iostream> // std::cout
int main () {
std::pair <int,int> foo;
std::pair <int,int> bar;
foo = std::make_pair (10,20);
bar = std::make_pair (10.5,'A'); // ok: implicit conversion from pair<double,char>
std::cout << "foo: " << foo.first << ", " << foo.second << '\n';
std::cout << "bar: " << bar.first << ", " << bar.second << '\n';
return 0;
}
Output:
foo: 10, 20 bar: 10, 65 |
forward
lvalue (1) | template <class T> T&& forward (typename remove_reference<T>::type& arg) noexcept; |
---|---|
rvalue (2) | template <class T> T&& forward (typename remove_reference<T>::type&& arg) noexcept; |
Forward argument
Returns an rvalue reference to arg if arg is not an lvalue reference.
If arg is an lvalue reference, the function returns arg without modifying its type.
This is a helper function to allow perfect forwarding of arguments taken as rvalue references to deduced types, preserving any potential move semantics involved.
The need for this function stems from the fact that all named values (such as function parameters) always evaluate as lvalues (even those declared as rvalue references), and this poses difficulties in preserving potential move semantics on template functions that forward arguments to other functions.
Both signatures return the same as:
static_cast<decltype(arg)&&>(arg)
By providing two signatures and using remove_reference on T, any instantiation is forced to explicitly specify the type of T (any implicitly deduced T would have no match).
// forward example
#include <utility> // std::forward
#include <iostream> // std::cout
// function with lvalue and rvalue reference overloads:
void overloaded (const int& x) {std::cout << "[lvalue]";}
void overloaded (int&& x) {std::cout << "[rvalue]";}
// function template taking rvalue reference to deduced type:
template <class T> void fn (T&& x) {
overloaded (x); // always an lvalue
overloaded (std::forward<T>(x)); // rvalue if argument is rvalue
}
int main () {
int a;
std::cout << "calling fn with lvalue: ";
fn (a);
std::cout << '\n';
std::cout << "calling fn with rvalue: ";
fn (0);
std::cout << '\n';
return 0;
}
Output:
calling fn with lvalue: [lvalue][lvalue] calling fn with rvalue: [lvalue][rvalue] |
move
template <class T>typename remove_reference<T>::type&& move (T&& arg) noexcept;
Move as rvalue
Returns an rvalue reference to arg.
This is a helper function to force move semantics on values, even if they have a name: Directly using the returned value causes arg to be considered an rvalue.
Generally, rvalues are values whose address cannot be obtained by dereferencing them, either because they are literals or because they are temporary in nature (such as values returned by functions or explicit constructor calls). By passing an object to this function, an rvalue that refers to it is obtained.
Many components of the standard library implement move semantics, allowing to transfer ownership of the assets and properties of an object directly without having to copy them when the argument is an rvalue.
Although note that -in the standard library- moving implies that the moved-from object is left in a valid but unspecified state. Which means that, after such an operation, the value of the moved-from object should only be destroyed or assigned a new value; accessing it otherwise yields an unspecified value.
The function returns the same as:
static_cast<remove_reference<decltype(arg)>::type&&>(arg)
Header <algorithm>
overloads this function, providing a similar behavior applied to ranges.
// move example
#include <utility> // std::move
#include <iostream> // std::cout
#include <vector> // std::vector
#include <string> // std::string
int main () {
std::string foo = "foo-string";
std::string bar = "bar-string";
std::vector<std::string> myvector;
myvector.push_back (foo); // copies
myvector.push_back (std::move(bar)); // moves
std::cout << "myvector contains:";
for (std::string& x:myvector) std::cout << ' ' << x;
std::cout << '\n';
return 0;
}
The first call to myvector.push_back copies the value of foo into the vector (foo keeps the value it had before the call).
The second call moves the value of bar into the vector. This transfers its content into the vector (while bar loses its value, and now is in a valid but unspecified state).
Output:
myvector contains: foo-string bar-string |
move_if_noexcept
template <class T> typename conditional < is_nothrow_move_constructible<T>::value || !is_copy_constructible<T>::value, T&&, const T& >::type move_if_noexcept(T& arg) noexcept;
Move if noexcept
Returns an rvalue reference to arg, unless copying is a better option than moving to provide at least a strong exception guarantee.
The strong guarantee is provided by operations that are guaranteed to leave objects in the same state they were before the operation in the case an exception is thrown. This is achieved either by operating on copies (so that the original object is left intact during the operation), or by using only operations that do not throw (in this case, a higher level of guarantee is provided: nothrow guarantee).
Some operations can be implemented both by moving or by copying objects, generally moving for rvalues and copying for lvalues: Moving is generally a more efficient operation than copying when the object is no longer needed (such as rvalues).
This function selects the type of an argument as an rvalue reference if the type is nothrow move-constructible (i.e., its move constructor never throws), or alternativelly, as an lvalue reference if the type is copy-constructible. If the type is neither, the function returns an rvalue, which will be selected for move-only types (even if they may throw).
The function returns the same as move(arg)
but with the return type casted to either T&&
or const T&
, depending on the properties of T.
// move_if_noexcept example
#include <utility> // std::move_if_noexcept
#include <iostream> // std::cout
// function with lvalue and rvalue reference overloads:
template <class T> void overloaded (T& x) {std::cout << "[lvalue]\n";}
template <class T> void overloaded (T&& x) {std::cout << "[rvalue]\n";}
struct A { // copyable + moveable (noexcept)
A() noexcept {}
A (const A&) noexcept {}
A (A&&) noexcept {}
};
struct B { // copyable + moveable (no noexcept)
B() {}
B (const B&) {}
B (B&&) {}
};
struct C { // moveable only (no noexcept)
C() {}
C (C&&) {}
};
int main () {
std::cout << "A: "; overloaded (std::move_if_noexcept(A()));
std::cout << "B: "; overloaded (std::move_if_noexcept(B()));
std::cout << "C: "; overloaded (std::move_if_noexcept(C()));
return 0;
}
A: [rvalue] B: [lvalue] C: [rvalue] |
declval
template <class T> typename add_rvalue_reference<T>::type declval() noexcept;
Declaration value
Returns an rvalue reference to type T without referring to any object.
This function shall only be used in unevaluated operands (such as the operands of sizeof
and decltype
).
T may be an incomplete type.
This is a helper function used to refer to members of a class in unevaluated operands, especially when either the constructor signature is unknown or when no objects of that type can be constructed (such as for abstract base classes).
// declval example
#include <utility> // std::declval
#include <iostream> // std::cout
struct A { // abstract class
virtual int value() = 0;
};
class B : public A { // class with specific constructor
int val_;
public:
B(int i,int j):val_(i*j){}
int value() {return val_;}
};
int main() {
decltype(std::declval<A>().value()) a; // int a
decltype(std::declval<B>().value()) b; // int b
decltype(B(0,0).value()) c; // same as above (known constructor)
a = b = B(10,2).value();
std::cout << a << '\n';
return 0;
}
Output:
20 |
pair
template <class T1, class T2> struct pair;
Pair of values
该类将一对值耦合在一起,这些值可以是不同类型(T1和T2)。可以首先通过其公共成员第一和第二个访问各个值。
pair是tuple的一种特殊情况。
piecewise_construct_t
struct piecewise_construct_t {};
Piecewise construct type
Piecewise construct常量的类型。 这是一种专门为重载特定对构造函数而设计的类型。 它是在header<utility>中定义的一个空类。此标头还定义了标准常量piecewise_struct,它是专门为调用重载构造函数而设计的此类值。
piecewise_construct
constexpr piecewise_construct_t piecewise_construct = piecewise_construct_t();
Piecewise construct constant
该常数值作为构造对对象的第一个参数传递,以通过将两个元组对象的元素转发到它们各自的构造函数来选择构造其成员的构造函数形式。
// piecewise_construct example
#include <utility> // std::pair, std::piecewise_construct
#include <iostream> // std::cout
#include <tuple> // std::forward_as_tuple
#include <vector> // std::vector
#include <string> // std::string
int main () {
std::pair < std::string, std::vector<int> >
foo (
std::piecewise_construct,
std::forward_as_tuple("test"),
std::forward_as_tuple(3,10)
);
std::cout << "foo.first: " << foo.first << '\n';
std::cout << "foo.second:";
for (int& x: foo.second) std::cout << ' ' << x;
std::cout << '\n';
return 0;
}
Output:
foo.first: test foo.second: 10 10 10 |
rel_ops
namespace rel_ops { template <class T> bool operator!= (const T& x, const T& y); template <class T> bool operator> (const T& x, const T& y); template <class T> bool operator<= (const T& x, const T& y); template <class T> bool operator>= (const T& x, const T& y);}
Relational Operators
此命名空间为四个关系运算符(!=、>、<=和>=)声明了模板函数,它们的行为源自运算符==(for!=)和运算符<(for>、<=and>=):
namespace rel_ops {
template <class T> bool operator!= (const T& x, const T& y) { return !(x==y); }
template <class T> bool operator> (const T& x, const T& y) { return y<x; }
template <class T> bool operator<= (const T& x, const T& y) { return !(y<x); }
template <class T> bool operator>= (const T& x, const T& y) { return !(x<y); }
}
这避免了为每个完整类型声明所有六个关系运算符的必要性;通过只定义两个操作符:operator==和operator<,并导入此命名空间,将为该类型定义所有六个操作符(不过,在不导入时,它们不会被依赖于参数的查找所选择)。
请注意,使用这个名称空间会为所有没有定义自己的类型的类型引入这些重载。尽管如此,由于非模板函数优先于模板函数,因此这些运算符中的任何一个都可能被定义为具有特定类型的不同行为。
// rel_ops example:
#include <iostream> // std::cout, std::boolalpha
#include <utility> // std::rel_ops
#include <cmath> // std::sqrt
class vector2d {
public:
double x,y;
vector2d (double px,double py): x(px), y(py) {}
double length() const {return std::sqrt(x*x+y*y);}
bool operator==(const vector2d& rhs) const {return length()==rhs.length();}
bool operator< (const vector2d& rhs) const {return length()< rhs.length();}
};
int main () {
using namespace std::rel_ops;
vector2d a (10,10); // length=14.14
vector2d b (15,5); // length=15.81
std::cout << std::boolalpha;
std::cout << "(a<b) is " << (a<b) << '\n';
std::cout << "(a>b) is " << (a>b) << '\n';
return 0;
}
Output:
(a<b) is true (a>b) is false |
Because we used rel_ops, all types for which operator< is defined (like vector2d) also have operator>.