C++20 to_address remove_cvref bind_front

C++20 to_address 等

    • 01 to_address
    • 02 remove_cvref
    • 03 bind_front

01 to_address


template< class Ptr >
constexpr auto to_address(const Ptr& p) noexcept;
template< class T >
constexpr T* to_address(T* p) noexcept;

to_address从缀饰指针获得裸指针(pointer_to 的反函数)
获得 p 所指向的地址,而不形成到被指向者的引用:

  1. 缀饰指针重载:若表达式 std::pointer_traits::to_address§ 为良式,则返回该表达式的结果。否则,返回 std::to_address(p.operator->()) 。
  2. 裸指针重载:若 T 为函数类型,则程序为病式,否则返回不修改的 p 。

参数:p - 缀饰或裸指针

注释: 即使在 p 引用的存储中无已构造的对象时,也能使用 std::to_address ,该情况下不能用 std::addressof(*p) ,因为无可绑定 std::addressof 参数的合法对象。1 2

// https://zh.cppreference.com/w/cpp/memory/to_address
// https://zh.cppreference.com/w/cpp/memory/pointer_traits/to_address
//// 可能实现
//template
//constexpr T* to_address(T* p) noexcept
//{
//    static_assert(!std::is_function_v);
//    return p;
//}
//
//template
//constexpr auto to_address(const T& p) noexcept
//{
//    if constexpr (requires{ std::pointer_traits::to_address(p); }) {
//        return std::pointer_traits::to_address(p);
//    }
//    else {
//        return std::to_address(p.operator->());
//    }
//}

template
auto allocator_new(A& a)
{
    auto p = a.allocate(1);
    try {
        std::allocator_traits::construct(a, std::to_address(p));
    }
    catch (...) {
        a.deallocate(p, 1);
        throw;
    }
    return p;
}

template
void allocator_delete(A& a, typename std::allocator_traits::pointer p)
{
    std::allocator_traits::destroy(a, std::to_address(p));
    a.deallocate(p, 1);
}

void test_to_address01() {
    std::allocator a;
    auto p = allocator_new(a);
    allocator_delete(a, p);
}

glenfe/to_address 3

// https://github.com/glenfe/to_address/blob/master/test/to_address_test.cpp
template
class P1 {
public:
    explicit P1(T* p)
        : p_(p) { }

    T* operator->() const noexcept {
        return p_;
    }

private:
    T* p_;
};

template
class P2 {
public:
    explicit P2(T* p)
        : p_(p) { }

    P1 operator->() const noexcept {
        return p_;
    }

private:
    P1 p_;
};

template
class P3 {
public:
    explicit P3(T* p)
        : p_(p) { }

    T* get() const noexcept {
        return p_;
    }

private:
    T* p_;
};

namespace std {

    template
    struct pointer_traits > {
        static T* to_address(const P3& p) noexcept {
            return p.get();
        }
    };
} // std

template
class P4 {
public:
    explicit P4(T* p)
        : p_(p) { }

    T* operator->() const noexcept {
        return nullptr;
    }

    T* get() const noexcept {
        return p_;
    }

private:
    int* p_;
};

namespace std {

    template
    struct pointer_traits > {
        static T* to_address(const P4& p) noexcept {
            return p.get();
        }
    };

} // std

void test_to_address02() {
    int i = 0;
    assert(std::to_address(&i) == &i);
    int* p = &i;
    assert(std::to_address(p) == &i);
    P1 p1(&i);
    assert(std::to_address(p1) == &i);
    P2 p2(&i);
    assert(std::to_address(p2) == &i);
    P3 p3(&i);
    assert(std::to_address(p3) == &i);
    P4 p4(&i);
    assert(std::to_address(p4) == &i);
}

02 remove_cvref

若类型 T 为引用类型,则提供成员 type ,它是移除了其最顶层 cv 限定符的 T 所引用的类型。否则 type 为移除最顶层 cv 限定符的 T 。

添加 remove_cvref 的特化的程序行为未定义。4

#include 
#include 
void test_remove_cvref() {
    std::cout << std::boolalpha
        << std::is_same_v, int> << '\n'
        << std::is_same_v, int> << '\n'
        << std::is_same_v, int> << '\n'
        << std::is_same_v, int> << '\n'
        << std::is_same_v, int[2]> << '\n'
        << std::is_same_v, int[2]> << '\n'
        << std::is_same_v, int(int)> << '\n';
}

03 bind_front

template 
constexpr /*unspecified*/ bind_front( F&& f, Args&&... args );

函数模板 bind_front 为 f 生成转发调用包装。调用此包装等价于绑定首 sizeof…(Args) 个参数到 args 再调用 f 。5

换言之 std::bind_front(f, bound_args…)(call_args…) 等价于 std::invoke(f, bound_args…, call_args…) 。

  1. f - 将绑定某些参数到的可调用 (Callable) 对象(函数对象、指向函数指针、到函数的引用、指向成员函数指针或指向数据成员指针)
  2. args - 参数列表,绑定首 sizeof…(Args) 个参数到 f

注释:有意令此函数取代 std::bind 。不同于 std::bind ,它不支持任意参数重排,而且不特别处理嵌套的 bind 表达式或 std::reference_wrapper 。另一方面,它注重调用包装对象的值类别,并传播底层调用运算符的异常规定。

如 std::invoke 中描述,调用指向非静态成员函数指针或指向非静态数据成员指针时,首参数必须是到要访问其成员的对象的引用或指针(可以包含智能指针,如 std::shared_ptr 与 std::unique_ptr )。

复制或移动给 std::bind_front 的参数,而决不按引用传递,除非用 std::ref 或 std::cref 包装它们。

#include 
#include 
int minus(int a, int b) {
    return a - b;
}
void test_bind_front() {
    auto fifty_minus = std::bind_front(&minus, 50);
    std::cout << fifty_minus(3);
}

https://github.com/5455945/cpp_demo/blob/master/C%2B%2B20/to_address/to_address.cpp


  1. to_address ↩︎

  2. std::pointer_traits::to_address ↩︎

  3. glenfe/to_address ↩︎

  4. remove_cvref ↩︎

  5. bind_front ↩︎

你可能感兴趣的:(c++)