C++20 to_address 等
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 所指向的地址,而不形成到被指向者的引用:
- 缀饰指针重载:若表达式 std::pointer_traits::to_address§ 为良式,则返回该表达式的结果。否则,返回 std::to_address(p.operator->()) 。
- 裸指针重载:若 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…) 。
- f - 将绑定某些参数到的可调用 (Callable) 对象(函数对象、指向函数指针、到函数的引用、指向成员函数指针或指向数据成员指针)
- 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