C++函数对象-引用包装器-可复制构造 (CopyConstructible) 且可复制赋值 (CopyAssignable) 的引用包装器(std::reference_wrapper)(一)

任何定义了函数调用操作符的对象都是函数对象。C++ 支持创建、操作新的函数对象,同时也提供了许多内置的函数对象。

引用包装器

引用包装器允许存储引用到可复制的函数对象中:

可复制构造 (CopyConstructible) 且可复制赋值 (CopyAssignable) 的引用包装器

std::reference_wrapper

template< class T >
class reference_wrapper;

(C++11 起)

std::reference_wrapper 是包装引用于可复制、可赋值对象的类模板。它常用作将容器存储入无法正常保有引用的标准容器(类似 std::vector )的机制。

特别是, std::reference_wrapper 是围绕到类型 T 的对象引用或函数引用的可复制构造 (CopyConstructible) 且可复制赋值 (CopyAssignable) 的包装器。 std::reference_wrapper 的实例是对象(它们可被复制或存储于容器),但它们能隐式转换成 T& ,故能以之为以引用接收底层类型的函数的参数。

若存储的引用可调用 (Callable) ,则可以相同参数调用 std::reference_wrapper

辅助函数 std::ref 与 std::cref 常用于生成 std::reference_wrapper 对象。

std::reference_wrapper 亦用于按引用传递对象给 std::bind 或 std::thread 的构造函数。

保证 std::reference_wrapper 可平凡复制 (TriviallyCopyable) 。

(C++17 起)

T 可为不完整类型。

(C++20 起)

成员类型

类型 定义
type T
result_type(C++17 中弃用)(C++20 中移除) T 为函数则为 T 的返回类型。否则不定义。
argument_type(C++17 中弃用)(C++20 中移除) 1) 若 T 为接受一个 A1 类型参数的函数或指向函数指针,则 argument_typeA1

2) 若 T 是不接收参数的指向类 T0 成员函数的指针,则 argument_typeT0* ,可能有 cv 限定
3) 若 T 是拥有成员类型 T::argument_type 的类类型,则 argument_type 为其别名

first_argument_type(C++17 中弃用)(C++20 中移除) 1) 若 T 是接收二个 A1A2 类型参数的函数或指向函数指针,则 first_argument_typeA1

2) 若 T 是接收一个参数的指向 T0 成员函数指针,则 first_argument_typeT0* ,可能有 cv 限定
3) 若 T 是拥有成员类型 T::first_argument_type 的类类型,则 first_argument_type 为其别名

second_argument_type(C++17 中弃用)(C++20 中移除) 1) 若 T 是接收二个 A1A2 类型参数的函数或指向函数指针,则 second_argument_typeA2

2) 若 T 是接收一个 A1 类型参数的指向 T0 成员函数指针,则 second_argument_typeA1 ,可有 cv 限定
3) 若 T 是拥有成员类型 T::second_argument_type 的类类型,则 second_argument_type 为其别名

成员函数

(构造函数)

存储引用于新的 std::reference_wrapper 对象
(公开成员函数)

operator=

重绑定 std::reference_wrapper
(公开成员函数)

getoperator T&

访问存储的引用
(公开成员函数)

operator()

调用其所存储的函数
(公开成员函数)

可能的实现

namespace detail
{
template  T& FUN(T& t) noexcept
{
    return t;
}
template  void FUN(T&&) = delete;
}

template 
class reference_wrapper
{
public:
    // 类型
    typedef T type;

    // 构造/复制/销毁
    template < class U, class = decltype(
                   detail::FUN(std::declval()),
                   std::enable_if_t < !std::is_same_v >> ()
                                                                                       ) >
    reference_wrapper(U && u) noexcept(noexcept(detail::FUN(std::forward(u))))
        : _ptr(std::addressof(detail::FUN(std::forward(u)))) {}
    reference_wrapper(const reference_wrapper&) noexcept = default;

    // 赋值
    reference_wrapper& operator=(const reference_wrapper& x) noexcept = default;

    // 访问
    operator T& () const noexcept
    {
        return *_ptr;
    }
    T& get() const noexcept
    {
        return *_ptr;
    }

    template< class... ArgTypes >
    std::invoke_result_t
    operator()(ArgTypes&&... args) const
    {
        return std::invoke(get(), std::forward(args)...);
    }

private:
    T* _ptr;
};

// 推导指引
template
reference_wrapper(T&) -> reference_wrapper;

调用示例 

#include 
#include 
#include 
#include 
#include 
#include 
#include 

struct Cell
{
    int x;
    int y;

    Cell() = default;
    Cell(int a, int b): x(a), y(b) {}
    Cell(const Cell &cell)
    {
        x = cell.x;
        y = cell.y;
    }

    bool operator <(const Cell &cell) const
    {
        if (x == cell.x)
        {
            return y < cell.y;
        }
        else
        {
            return x < cell.x;
        }
    }

    Cell &operator+(const Cell &cell)
    {
        x += cell.x;
        y += cell.y;
        return *this;
    }

    Cell &operator*=(int n)
    {
        x *= n;
        y *= n;
        return *this;
    }

    Cell &operator++()
    {
        x += 1;
        y += 1;
        return *this;
    }
};

std::ostream &operator<<(std::ostream &os, const Cell &cell)
{
    os << "{" << cell.x << "," << cell.y << "}";
    return os;
}

int main()
{
    std::list list1(10);
    std::iota(list1.begin(), list1.end(), Cell{101, 102});

    std::vector> vector1(list1.begin(), list1.end());
    // 不能在 list 上用 shuffle (要求随机访问),但能在 vector 上使用它
    std::shuffle(vector1.begin(), vector1.end(), std::mt19937{std::random_device{}()});

    std::cout << "Contents of the list: " << std::endl;
    for (const Cell &cell : list1)
    {
        std::cout << cell << ' ';
    }
    std::cout << std::endl << std::endl;

    std::cout << "Contents of the list, as seen through a shuffled vector: " << std::endl;
    for (const Cell &cell : vector1)
    {
        std::cout << cell << ' ';
    }
    std::cout << std::endl << std::endl;

    std::cout << "Doubling the values in the initial list..." << std::endl;
    for (Cell &cell : list1)
    {
        cell *= 2;
    }
    for (const Cell &cell : list1)
    {
        std::cout << cell << ' ';
    }
    std::cout << std::endl << std::endl;

    std::cout << "Contents of the list, as seen through a shuffled vector: " << std::endl;
    for (const Cell &cell : vector1)
    {
        std::cout << cell << ' ';
    }
    std::cout << std::endl << std::endl;

    return 0;
}

输出

你可能感兴趣的:(#,引用包装器,c++,标准库模板,STL,函数对象,引用包装器,reference,_wrapper)