STL-utility

Utility components

This header contains utilities in unrelated domains:

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>.

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值