C++20 to_address remove_cvref bind_front

01 to_address

<memory>
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<class T>
//constexpr T* to_address(T* p) noexcept
//{
//    static_assert(!std::is_function_v<T>);
//    return p;
//}
//
//template<class T>
//constexpr auto to_address(const T& p) noexcept
//{
//    if constexpr (requires{ std::pointer_traits<T>::to_address(p); }) {
//        return std::pointer_traits<T>::to_address(p);
//    }
//    else {
//        return std::to_address(p.operator->());
//    }
//}

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

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

void test_to_address01() {
    std::allocator<int> 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 T>
class P1 {
public:
    explicit P1(T* p)
        : p_(p) { }

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

private:
    T* p_;
};

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

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

private:
    P1<T> p_;
};

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

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

private:
    T* p_;
};

namespace std {

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

template<class T>
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<class T>
    struct pointer_traits<P4<T> > {
        static T* to_address(const P4<T>& 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<int> p1(&i);
    assert(std::to_address(p1) == &i);
    P2<int> p2(&i);
    assert(std::to_address(p2) == &i);
    P3<int> p3(&i);
    assert(std::to_address(p3) == &i);
    P4<int> p4(&i);
    assert(std::to_address(p4) == &i);
}

02 remove_cvref

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

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

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

03 bind_front

template <class F, class... Args>
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 <functional>
#include <iostream>
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 ↩︎

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值