proxy<F>::proxy
// (1)
proxy() noexcept = default;
proxy(std::nullptr_t) noexcept;
// (2)
proxy(const proxy&) noexcept requires(F::copyability ==
constraint_level::trivial) = default;
proxy(const proxy& rhs)
noexcept(F::copyability == constraint_level::nothrow)
requires(F::copyability == constraint_level::nontrivial ||
F::copyability == constraint_level::nothrow);
// (3)
proxy(proxy&& rhs)
noexcept(F::relocatability == constraint_level::nothrow)
requires(F::relocatability >= constraint_level::nontrivial &&
F::copyability != constraint_level::trivial);
// (4)
template <class P>
proxy(P&& ptr) noexcept(std::is_nothrow_constructible_v<std::decay_t<P>, P>)
requires(std::is_constructible_v<std::decay_t<P>, P>);
// (5)
template <class P, class... Args>
explicit proxy(std::in_place_type_t<P>, Args&&... args)
noexcept(std::is_nothrow_constructible_v<P, Args...>)
requires(std::is_constructible_v<P, Args...>);
// (6)
template <class P, class U, class... Args>
explicit proxy(std::in_place_type_t<P>, std::initializer_list<U> il,
Args&&... args)
noexcept(std::is_nothrow_constructible_v<
P, std::initializer_list<U>&, Args...>)
requires(std::is_constructible_v<P, std::initializer_list<U>&, Args...>);
Creates a new proxy.
(1)Default constructor and the constructor takingnullptrconstruct aproxythat does not contain a value.(2)Copy constructor constructs aproxywhose contained value is that ofrhsifrhscontains a value, or otherwise, constructs aproxythat does not contain a value. As per therequiresclause, the copy constructor is trivial whenF::copyability == constraint_level::trivial.(3)Move constructor constructs aproxywhose contained value is that ofrhsifrhscontains a value, or otherwise, constructs aproxythat does not contain a value.rhsis in a valid but unspecified state after move construction. As per therequiresclause, the move constructor does not participate in overload resolution whenF::copyability == constraint_level::trivial, so that a move construction falls back to the trivial copy constructor.(4)LetVPbestd::decay_t<P>. Constructor taking a value of pointer constructs aproxywhose contained value is of typeVPand direct-non-list-initialized withstd::forward<P>(ptr). This overload participates in overload resolution only ifstd::decay_t<P>is not a specialization ofproxynor a specialization ofstd::in_place_type_t.(5)Constructs aproxywhose contained value is of typePand direct-non-list-initialized withstd::forward<Args>(args)....(6)Constructs aproxywhose contained value is of typePand direct-non-list-initialized withil, std::forward<Args>(args)....
Since 3.3.0: For (4-6), if proxiable<std::decay_t<P>, F> is false, the program is ill-formed and diagnostic messages are generated.
Comparing with Other Standard Polymorphic Wrappers
The constructors of proxy<F> are similar to but have certain differences from other polymorphic wrappers in the standard, specifically, std::any, and std::move_only_function.
std::function was introduced in C++11. Comparing its constructors with proxy:
- It forces the target type to be copy constructible, even if its copy constructor is not used in a certain context (which motivated the introduction of
std::move_only_functionin C++23). - It only supports
Callabletypes, whileproxysupports any pointer type that satisfiesproxiable<P, F>. - It does not have overloads that take
std::in_place_type_tto construct a value in-place. - It does not have a conditional default copy constructor, which is efficient for trivial types.
- It used to have several overloads that took an additional allocator, but these were removed in C++17 because "the semantics are unclear, and there are technical issues with storing an allocator in a type-erased context and then recovering that allocator later for any allocations needed during copy assignment". Although the constructors of
proxydo not explicitly take custom allocator types, we believe this is a useful scenario and provided full support viaallocate_proxy. - It forces
noexceptspecifiers, which is mitigated instd::move_only_function.
std::any was introduced in C++17. Comparing its constructors with proxy:
- Similar with
std::function, It forces the target type to be copy constructible, even if its copy constructor is not used in a certain context. - Similar with
std::function, it does not have a conditional default copy constructor, which is efficient for trivial types. - It does not support allocators.
- Similar with
std::function, it forcesnoexceptspecifiers. - In the overloads that take
std::in_place_type_t<T>to construct a value in-place, the value type is obtained viastd::decay_t<T>rather than bareT, which complicates the semantics and we do not believe is useful.
std::move_only_function was introduced in C++23. Comparing its constructors with proxy:
- As its name suggests, it is not copyable at all.
proxyis conditionally copyable depending on the implementation of the given facade typeF. - Similar with
std::function, it only supportsCallabletypes, whileproxysupport any pointer type that satisfiesproxiable<P, F>. - Similar with
std::any, it does not support allocators. - Similar with
std::any, in the overloads that takestd::in_place_type_t<T>to construct a value in-place, the value type is obtained viastd::decay_t<T>rather than bareT, which complicates the semantics and we do not believe is useful.
Example
#include <deque>
#include <iostream>
#include <vector>
#include <proxy/proxy.h>
PRO_DEF_MEM_DISPATCH(MemSize, size);
PRO_DEF_MEM_DISPATCH(MemClear, clear);
struct BasicContainer
: pro::facade_builder //
::add_convention<MemSize, std::size_t() const & noexcept> //
::add_convention<MemClear, void() noexcept> //
::support_copy<pro::constraint_level::nontrivial> //
::build {};
int main() {
std::vector<int> v{1, 2, 3};
pro::proxy<BasicContainer> p0;
std::cout << std::boolalpha << p0.has_value() << "\n"; // Prints "false"
// Construct a proxy with a raw pointer
pro::proxy<BasicContainer> p1 = &v;
std::cout << p1.has_value() << ", " << p1->size() << "\n"; // Prints "true,3"
// Construct a proxy with a smart pointer
pro::proxy<BasicContainer> p2 = std::make_shared<std::deque<double>>(10);
std::cout << p2.has_value() << ", " << p2->size() << "\n"; // Prints "true,10"
// Copy construction
pro::proxy<BasicContainer> p3 = p2;
std::cout << p3.has_value() << ", " << p3->size() << "\n"; // Prints "true,10"
// Move construction
pro::proxy<BasicContainer> p4 = std::move(p3);
std::cout << p4.has_value() << ", " << p4->size() << "\n"; // Prints "true,10"
// p3 no longer contains a value
std::cout << p3.has_value() << "\n"; // Prints "false"
// p2 and p4 shares the same object of std::deque<double>
p2->clear();
std::cout << p4.has_value() << ", " << p4->size() << "\n"; // Prints "true,0"
}