mirror of
https://github.com/davisking/dlib.git
synced 2024-11-01 10:14:53 +08:00
dlib::optional (#2815)
* backport of std::optional for c++11. WIP * wip * whoops * wip * wip * holy shit this is tiring * type traits * more traits * more traits * wip * delimiters * bad header include * noexcept correctness * fixed has_value() more tests * started adding monadic intefaces * compile time contract * added transform * added or_else * according to the standard, swap also empties objects when necessary * noexcept tests * beginning monad tests * bug fix * contexpr monads * added docs * more tests and bug fixes * - bug fix - test inplace constructors and emplace() * test value_or() * more tests --------- Co-authored-by: pf <pf@me> Co-authored-by: Your Name <you@example.com>
This commit is contained in:
parent
4d2db66cc5
commit
4e22715eb5
1017
dlib/optional.h
Normal file
1017
dlib/optional.h
Normal file
File diff suppressed because it is too large
Load Diff
@ -161,6 +161,7 @@ add_executable(${target_name} main.cpp tester.cpp
|
||||
elastic_net.cpp
|
||||
te.cpp
|
||||
ffmpeg.cpp
|
||||
optional.cpp
|
||||
)
|
||||
|
||||
get_filename_component(DLIB_FFMPEG_DATA ${CMAKE_SOURCE_DIR}/ffmpeg_data/details.cfg REALPATH)
|
||||
|
465
dlib/test/optional.cpp
Normal file
465
dlib/test/optional.cpp
Normal file
@ -0,0 +1,465 @@
|
||||
// Copyright (C) 2023 Davis E. King (davis@dlib.net)
|
||||
// License: Boost Software License See LICENSE.txt for the full license.
|
||||
|
||||
#include <dlib/optional.h>
|
||||
#include "tester.h"
|
||||
|
||||
namespace
|
||||
{
|
||||
using namespace test;
|
||||
using namespace dlib;
|
||||
|
||||
logger dlog("test.optional");
|
||||
|
||||
// ---------------------------------------------------------------------------------------------------
|
||||
|
||||
static_assert(std::is_copy_constructible<dlib::optional<int>>::value, "bad");
|
||||
static_assert(std::is_copy_assignable<dlib::optional<int>>::value, "bad");
|
||||
static_assert(std::is_move_constructible<dlib::optional<int>>::value, "bad");
|
||||
static_assert(std::is_move_assignable<dlib::optional<int>>::value, "bad");
|
||||
static_assert(std::is_destructible<dlib::optional<int>>::value, "bad");
|
||||
|
||||
static_assert(std::is_trivially_copy_constructible<dlib::optional<int>>::value, "bad");
|
||||
static_assert(std::is_trivially_copy_assignable<dlib::optional<int>>::value, "bad");
|
||||
static_assert(std::is_trivially_move_constructible<dlib::optional<int>>::value, "bad");
|
||||
static_assert(std::is_trivially_move_assignable<dlib::optional<int>>::value, "bad");
|
||||
static_assert(std::is_trivially_destructible<dlib::optional<int>>::value, "bad");
|
||||
|
||||
static_assert(std::is_nothrow_copy_constructible<dlib::optional<int>>::value, "bad");
|
||||
static_assert(std::is_nothrow_copy_assignable<dlib::optional<int>>::value, "bad");
|
||||
static_assert(std::is_nothrow_move_constructible<dlib::optional<int>>::value, "bad");
|
||||
static_assert(std::is_nothrow_move_assignable<dlib::optional<int>>::value, "bad");
|
||||
static_assert(std::is_nothrow_destructible<dlib::optional<int>>::value, "bad");
|
||||
|
||||
struct trivial_type
|
||||
{
|
||||
trivial_type(const trivial_type&) = default;
|
||||
trivial_type(trivial_type&&) = default;
|
||||
trivial_type& operator=(const trivial_type&) = default;
|
||||
trivial_type& operator=(trivial_type&&) = default;
|
||||
~trivial_type() = default;
|
||||
};
|
||||
|
||||
static_assert(std::is_copy_constructible<dlib::optional<trivial_type>>::value, "bad");
|
||||
static_assert(std::is_copy_assignable<dlib::optional<trivial_type>>::value, "bad");
|
||||
static_assert(std::is_move_constructible<dlib::optional<trivial_type>>::value, "bad");
|
||||
static_assert(std::is_move_assignable<dlib::optional<trivial_type>>::value, "bad");
|
||||
static_assert(std::is_destructible<dlib::optional<trivial_type>>::value, "bad");
|
||||
|
||||
static_assert(std::is_trivially_copy_constructible<dlib::optional<trivial_type>>::value, "bad");
|
||||
static_assert(std::is_trivially_copy_assignable<dlib::optional<trivial_type>>::value, "bad");
|
||||
static_assert(std::is_trivially_move_constructible<dlib::optional<trivial_type>>::value, "bad");
|
||||
static_assert(std::is_trivially_move_assignable<dlib::optional<trivial_type>>::value, "bad");
|
||||
static_assert(std::is_trivially_destructible<dlib::optional<trivial_type>>::value, "bad");
|
||||
|
||||
static_assert(std::is_nothrow_copy_constructible<dlib::optional<trivial_type>>::value, "bad");
|
||||
static_assert(std::is_nothrow_copy_assignable<dlib::optional<trivial_type>>::value, "bad");
|
||||
static_assert(std::is_nothrow_move_constructible<dlib::optional<trivial_type>>::value, "bad");
|
||||
static_assert(std::is_nothrow_move_assignable<dlib::optional<trivial_type>>::value, "bad");
|
||||
static_assert(std::is_nothrow_destructible<dlib::optional<trivial_type>>::value, "bad");
|
||||
|
||||
struct non_trivial_type
|
||||
{
|
||||
non_trivial_type(const non_trivial_type&) {}
|
||||
non_trivial_type(non_trivial_type&&) {};
|
||||
non_trivial_type& operator=(const non_trivial_type&) { return *this; }
|
||||
non_trivial_type& operator=(non_trivial_type&&) { return *this; };
|
||||
~non_trivial_type() {}
|
||||
};
|
||||
|
||||
static_assert(std::is_copy_constructible<dlib::optional<non_trivial_type>>::value, "bad");
|
||||
static_assert(std::is_copy_assignable<dlib::optional<non_trivial_type>>::value, "bad");
|
||||
static_assert(std::is_move_constructible<dlib::optional<non_trivial_type>>::value, "bad");
|
||||
static_assert(std::is_move_assignable<dlib::optional<non_trivial_type>>::value, "bad");
|
||||
static_assert(std::is_destructible<dlib::optional<non_trivial_type>>::value, "bad");
|
||||
|
||||
static_assert(!std::is_trivially_copy_constructible<dlib::optional<non_trivial_type>>::value, "bad");
|
||||
static_assert(!std::is_trivially_copy_assignable<dlib::optional<non_trivial_type>>::value, "bad");
|
||||
static_assert(!std::is_trivially_move_constructible<dlib::optional<non_trivial_type>>::value, "bad");
|
||||
static_assert(!std::is_trivially_move_assignable<dlib::optional<non_trivial_type>>::value, "bad");
|
||||
static_assert(!std::is_trivially_destructible<dlib::optional<non_trivial_type>>::value, "bad");
|
||||
|
||||
static_assert(!std::is_nothrow_copy_constructible<dlib::optional<non_trivial_type>>::value, "bad");
|
||||
static_assert(!std::is_nothrow_copy_assignable<dlib::optional<non_trivial_type>>::value, "bad");
|
||||
static_assert(!std::is_nothrow_move_constructible<dlib::optional<non_trivial_type>>::value, "bad");
|
||||
static_assert(!std::is_nothrow_move_assignable<dlib::optional<non_trivial_type>>::value, "bad");
|
||||
|
||||
struct nothing_works
|
||||
{
|
||||
nothing_works(const nothing_works&) = delete;
|
||||
nothing_works(nothing_works&&) = delete;
|
||||
nothing_works& operator=(const nothing_works&) = delete;
|
||||
nothing_works& operator=(nothing_works&&) = delete;
|
||||
};
|
||||
|
||||
static_assert(!std::is_copy_constructible<dlib::optional<nothing_works>>::value, "bad");
|
||||
static_assert(!std::is_copy_assignable<dlib::optional<nothing_works>>::value, "bad");
|
||||
static_assert(!std::is_move_constructible<dlib::optional<nothing_works>>::value, "bad");
|
||||
static_assert(!std::is_move_assignable<dlib::optional<nothing_works>>::value, "bad");
|
||||
|
||||
struct copyable_type
|
||||
{
|
||||
copyable_type(const copyable_type&) = default;
|
||||
copyable_type(copyable_type&&) = delete;
|
||||
copyable_type& operator=(const copyable_type&) = default;
|
||||
copyable_type& operator=(copyable_type&&) = delete;
|
||||
};
|
||||
|
||||
static_assert(std::is_copy_constructible<dlib::optional<copyable_type>>::value, "bad");
|
||||
static_assert(std::is_copy_assignable<dlib::optional<copyable_type>>::value, "bad");
|
||||
//copyable_type can still be moved, but it will just copy.
|
||||
|
||||
struct moveable_type
|
||||
{
|
||||
moveable_type(const moveable_type&) = delete;
|
||||
moveable_type(moveable_type&&) = default;
|
||||
moveable_type& operator=(const moveable_type&) = delete;
|
||||
moveable_type& operator=(moveable_type&&) = default;
|
||||
};
|
||||
|
||||
static_assert(!std::is_copy_constructible<dlib::optional<moveable_type>>::value, "bad");
|
||||
static_assert(!std::is_copy_assignable<dlib::optional<moveable_type>>::value, "bad");
|
||||
static_assert(std::is_move_constructible<dlib::optional<moveable_type>>::value, "bad");
|
||||
static_assert(std::is_move_assignable<dlib::optional<moveable_type>>::value, "bad");
|
||||
|
||||
// ---------------------------------------------------------------------------------------------------
|
||||
|
||||
void test_optional_int()
|
||||
{
|
||||
dlib::optional<int> o1;
|
||||
DLIB_TEST(!o1);
|
||||
DLIB_TEST(!o1.has_value());
|
||||
DLIB_TEST(o1.value_or(2) == 2);
|
||||
int throw_counter{0};
|
||||
try {
|
||||
o1.value();
|
||||
} catch(const std::exception& e) {
|
||||
throw_counter++;
|
||||
}
|
||||
DLIB_TEST(throw_counter == 1);
|
||||
DLIB_TEST(o1 == dlib::nullopt);
|
||||
|
||||
dlib::optional<int> o2 = dlib::nullopt;
|
||||
static_assert(noexcept(o2 = dlib::nullopt), "bad");
|
||||
DLIB_TEST(!o2);
|
||||
DLIB_TEST(!o2.has_value());
|
||||
DLIB_TEST(o2.value_or(3) == 3);
|
||||
|
||||
dlib::optional<int> o3 = 42;
|
||||
DLIB_TEST(*o3 == 42);
|
||||
DLIB_TEST(o3.value() == 42);
|
||||
DLIB_TEST(o3.value_or(3) == 42);
|
||||
DLIB_TEST(o3 == 42);
|
||||
DLIB_TEST(42 == o3);
|
||||
|
||||
dlib::optional<int> o4 = o3;
|
||||
static_assert(noexcept(o4 = o3), "bad");
|
||||
DLIB_TEST(*o3 == 42);
|
||||
DLIB_TEST(*o4 == 42);
|
||||
DLIB_TEST(o4 == 42);
|
||||
DLIB_TEST(42 == o4);
|
||||
DLIB_TEST(o4.value() == 42);
|
||||
|
||||
dlib::optional<int> o5 = o1;
|
||||
static_assert(noexcept(o5 = o1), "bad");
|
||||
DLIB_TEST(!o1);
|
||||
DLIB_TEST(!o5);
|
||||
DLIB_TEST(!o5.has_value());
|
||||
|
||||
dlib::optional<int> o6 = std::move(o3);
|
||||
static_assert(noexcept(o6 = std::move(o3)), "bad");
|
||||
DLIB_TEST(*o6 == 42);
|
||||
DLIB_TEST(o6.value() == 42);
|
||||
|
||||
dlib::optional<short> o7 = (short)42;
|
||||
static_assert(noexcept(o7 = (short)42), "bad");
|
||||
DLIB_TEST(*o7 == 42);
|
||||
|
||||
dlib::optional<int> o8 = o7;
|
||||
DLIB_TEST(*o7 == 42);
|
||||
DLIB_TEST(*o8 == 42);
|
||||
|
||||
dlib::optional<int> o9 = std::move(o7);
|
||||
DLIB_TEST(*o9 == 42);
|
||||
|
||||
dlib::optional<int> o10;
|
||||
DLIB_TEST(!o10);
|
||||
o10 = o3;
|
||||
DLIB_TEST(*o10 == 42);
|
||||
o10 = dlib::nullopt;
|
||||
DLIB_TEST(!o10);
|
||||
|
||||
dlib::optional<int> o11;
|
||||
o11 = std::move(o3);
|
||||
DLIB_TEST(*o11 == 42);
|
||||
o11 = (short)12;
|
||||
DLIB_TEST(*o11 == 12);
|
||||
|
||||
dlib::optional<int> o12;
|
||||
swap(o12, o4);
|
||||
DLIB_TEST(o12);
|
||||
DLIB_TEST(!o4);
|
||||
DLIB_TEST(*o12 == 42);
|
||||
static_assert(noexcept(swap(o12, o4)), "bad");
|
||||
|
||||
o4.reset();
|
||||
DLIB_TEST(!o4);
|
||||
}
|
||||
|
||||
// ---------------------------------------------------------------------------------------------------
|
||||
|
||||
void test_optional_int_constexpr()
|
||||
{
|
||||
{
|
||||
constexpr dlib::optional<int> o2{};
|
||||
constexpr dlib::optional<int> o3 = {};
|
||||
constexpr dlib::optional<int> o4 = dlib::nullopt;
|
||||
constexpr dlib::optional<int> o5 = {dlib::nullopt};
|
||||
constexpr dlib::optional<int> o6(dlib::nullopt);
|
||||
|
||||
static_assert(!o2, "bad");
|
||||
static_assert(!o3, "bad");
|
||||
static_assert(!o4, "bad");
|
||||
static_assert(!o5, "bad");
|
||||
static_assert(!o6, "bad");
|
||||
|
||||
static_assert(o2.value_or(1) == 1, "bad");
|
||||
static_assert(o3.value_or(1) == 1, "bad");
|
||||
static_assert(o4.value_or(1) == 1, "bad");
|
||||
static_assert(o5.value_or(1) == 1, "bad");
|
||||
static_assert(o6.value_or(1) == 1, "bad");
|
||||
|
||||
static_assert(o2 != 1, "bad");
|
||||
static_assert(1 != o2, "bad");
|
||||
}
|
||||
}
|
||||
|
||||
// ---------------------------------------------------------------------------------------------------
|
||||
|
||||
void test_optional_int_monads()
|
||||
{
|
||||
dlib::optional<int> o1{42};
|
||||
|
||||
{
|
||||
auto res = o1.and_then([](int i) { return dlib::optional<long>(i); });
|
||||
|
||||
static_assert(std::is_same<decltype(res), dlib::optional<long>>::value, "bad map");
|
||||
DLIB_TEST(*res == 42);
|
||||
}
|
||||
|
||||
{
|
||||
auto res = o1.transform([](int i) { return (long)i; });
|
||||
|
||||
static_assert(std::is_same<decltype(res), dlib::optional<long>>::value, "bad map");
|
||||
DLIB_TEST(*res == 42);
|
||||
}
|
||||
|
||||
{
|
||||
auto res = o1.and_then([](int i) { return dlib::optional<std::string>(std::to_string(i)); });
|
||||
|
||||
static_assert(std::is_same<decltype(res), dlib::optional<std::string>>::value, "bad map");
|
||||
DLIB_TEST(*res == "42");
|
||||
}
|
||||
|
||||
{
|
||||
auto res = o1.transform([](int i) { return std::to_string(i); });
|
||||
|
||||
static_assert(std::is_same<decltype(res), dlib::optional<std::string>>::value, "bad map");
|
||||
DLIB_TEST(*res == "42");
|
||||
}
|
||||
|
||||
{
|
||||
auto res = o1.transform([](int i) {return i+1;})
|
||||
.transform([](int i) {return i*2;})
|
||||
.transform([](int i) {return std::to_string(i);})
|
||||
.transform([](const std::string& str) {return std::stoi(str);})
|
||||
.transform([](int i) {return i - 2;})
|
||||
.or_else([] {return dlib::make_optional<int>(0);});
|
||||
|
||||
static_assert(std::is_same<decltype(res), dlib::optional<int>>::value, "bad map");
|
||||
DLIB_TEST(*res == 84);
|
||||
}
|
||||
|
||||
dlib::optional<int> o2;
|
||||
|
||||
{
|
||||
auto res = o2.transform([](int i) {return i+1;})
|
||||
.or_else([] {return dlib::make_optional<int>(0);});
|
||||
|
||||
static_assert(std::is_same<decltype(res), dlib::optional<int>>::value, "bad map");
|
||||
DLIB_TEST(*res == 0);
|
||||
}
|
||||
}
|
||||
|
||||
// ---------------------------------------------------------------------------------------------------
|
||||
|
||||
void test_optional_int_constexpr_monads()
|
||||
{
|
||||
constexpr dlib::optional<int> o1{42};
|
||||
|
||||
{
|
||||
struct callback
|
||||
{
|
||||
constexpr auto operator()(int i) {return dlib::optional<long>{i}; };
|
||||
};
|
||||
|
||||
constexpr auto res = o1.and_then(callback{});
|
||||
|
||||
static_assert(std::is_same<std::decay_t<decltype(res)>, dlib::optional<long>>::value, "bad map");
|
||||
static_assert(*res == 42, "bad");
|
||||
DLIB_TEST(*res == 42);
|
||||
}
|
||||
}
|
||||
|
||||
// ---------------------------------------------------------------------------------------------------
|
||||
|
||||
static int constructor_count{0};
|
||||
static int copy_constructor_count{0};
|
||||
static int move_constructor_count{0};
|
||||
static int copy_assign_count{0};
|
||||
static int move_assign_count{0};
|
||||
|
||||
static void reset_counters()
|
||||
{
|
||||
constructor_count = 0;
|
||||
copy_constructor_count = 0;
|
||||
move_constructor_count = 0;
|
||||
copy_assign_count = 0;
|
||||
move_assign_count = 0;
|
||||
}
|
||||
|
||||
struct optional_dummy
|
||||
{
|
||||
optional_dummy() {++constructor_count;}
|
||||
optional_dummy(const optional_dummy&) {++copy_constructor_count;}
|
||||
optional_dummy(optional_dummy&&) {++move_constructor_count;}
|
||||
optional_dummy& operator=(const optional_dummy&) {++copy_assign_count; return *this;}
|
||||
optional_dummy& operator=(optional_dummy&&) {++move_assign_count; return *this;}
|
||||
};
|
||||
|
||||
void test_constructors()
|
||||
{
|
||||
dlib::optional<optional_dummy> val;
|
||||
DLIB_TEST(constructor_count == 0);
|
||||
DLIB_TEST(copy_constructor_count == 0);
|
||||
DLIB_TEST(move_constructor_count == 0);
|
||||
DLIB_TEST(copy_assign_count == 0);
|
||||
DLIB_TEST(move_assign_count == 0);
|
||||
|
||||
val = optional_dummy{};
|
||||
DLIB_TEST(constructor_count == 1);
|
||||
DLIB_TEST(copy_constructor_count == 0);
|
||||
DLIB_TEST(move_constructor_count == 1);
|
||||
DLIB_TEST(copy_assign_count == 0);
|
||||
DLIB_TEST(move_assign_count == 0);
|
||||
reset_counters();
|
||||
|
||||
dlib::optional<optional_dummy> val2{val};
|
||||
DLIB_TEST(constructor_count == 0);
|
||||
DLIB_TEST(copy_constructor_count == 1);
|
||||
DLIB_TEST(move_constructor_count == 0);
|
||||
DLIB_TEST(copy_assign_count == 0);
|
||||
DLIB_TEST(move_assign_count == 0);
|
||||
reset_counters();
|
||||
|
||||
dlib::optional<optional_dummy> val3{std::move(val)};
|
||||
DLIB_TEST(constructor_count == 0);
|
||||
DLIB_TEST(copy_constructor_count == 0);
|
||||
DLIB_TEST(move_constructor_count == 1);
|
||||
DLIB_TEST(copy_assign_count == 0);
|
||||
DLIB_TEST(move_assign_count == 0);
|
||||
reset_counters();
|
||||
|
||||
val2 = val;
|
||||
DLIB_TEST(constructor_count == 0);
|
||||
DLIB_TEST(copy_constructor_count == 0);
|
||||
DLIB_TEST(move_constructor_count == 0);
|
||||
DLIB_TEST(copy_assign_count == 1);
|
||||
DLIB_TEST(move_assign_count == 0);
|
||||
reset_counters();
|
||||
|
||||
val3 = std::move(val);
|
||||
DLIB_TEST(constructor_count == 0);
|
||||
DLIB_TEST(copy_constructor_count == 0);
|
||||
DLIB_TEST(move_constructor_count == 0);
|
||||
DLIB_TEST(copy_assign_count == 0);
|
||||
DLIB_TEST(move_assign_count == 1);
|
||||
reset_counters();
|
||||
}
|
||||
|
||||
// ---------------------------------------------------------------------------------------------------
|
||||
|
||||
void test_emplace()
|
||||
{
|
||||
struct A
|
||||
{
|
||||
A() = default;
|
||||
explicit A(int i, float f, const std::string& str) : i_{i}, f_{f}, str_{str} {}
|
||||
|
||||
int i_{0};
|
||||
float f_{0.0f};
|
||||
std::string str_;
|
||||
};
|
||||
|
||||
dlib::optional<A> o1(dlib::in_place, 1, 2.5f, "hello there");
|
||||
DLIB_TEST(o1);
|
||||
DLIB_TEST(o1->i_ == 1);
|
||||
DLIB_TEST(o1->f_ == 2.5f);
|
||||
DLIB_TEST(o1->str_ == "hello there");
|
||||
|
||||
dlib::optional<A> o2;
|
||||
o2.emplace(2, 5.1f, "general kenobi");
|
||||
DLIB_TEST(o2);
|
||||
DLIB_TEST(o2->i_ == 2);
|
||||
DLIB_TEST(o2->f_ == 5.1f);
|
||||
DLIB_TEST(o2->str_ == "general kenobi");
|
||||
|
||||
auto o3 = dlib::make_optional<A>(3, 3.141592f, "from a certain point of view");
|
||||
DLIB_TEST(o3);
|
||||
DLIB_TEST(o3->i_ == 3);
|
||||
DLIB_TEST(o3->f_ == 3.141592f);
|
||||
DLIB_TEST(o3->str_ == "from a certain point of view");
|
||||
|
||||
dlib::optional<std::string> o4(dlib::in_place, {'a', 'b', 'c'});
|
||||
DLIB_TEST(o4);
|
||||
DLIB_TEST(*o4 == "abc");
|
||||
DLIB_TEST(o4 == "abc");
|
||||
|
||||
dlib::optional<std::string> o5;
|
||||
o5.emplace({'a', 'b', 'c'});
|
||||
DLIB_TEST(o5);
|
||||
DLIB_TEST(*o5 == "abc");
|
||||
}
|
||||
|
||||
// ---------------------------------------------------------------------------------------------------
|
||||
|
||||
void test_make_optional()
|
||||
{
|
||||
constexpr auto o1 = dlib::make_optional(1);
|
||||
static_assert(std::is_same<std::decay_t<decltype(o1)>, dlib::optional<int>>::value, "bad");
|
||||
static_assert(o1 == 1, "bad");
|
||||
static_assert(1 == o1, "bad");
|
||||
}
|
||||
|
||||
class optional_tester : public tester
|
||||
{
|
||||
public:
|
||||
optional_tester (
|
||||
) :
|
||||
tester ("test_optional",
|
||||
"Runs tests on the optional object")
|
||||
{}
|
||||
|
||||
void perform_test (
|
||||
)
|
||||
{
|
||||
test_optional_int();
|
||||
test_optional_int_constexpr();
|
||||
test_optional_int_monads();
|
||||
test_optional_int_constexpr_monads();
|
||||
test_constructors();
|
||||
test_emplace();
|
||||
test_make_optional();
|
||||
}
|
||||
} a;
|
||||
}
|
@ -4,7 +4,7 @@
|
||||
#define DLIB_UTILITY_Hh_
|
||||
|
||||
#include <cstddef>
|
||||
#include <type_traits>
|
||||
#include <utility>
|
||||
|
||||
/*
|
||||
This header contains back-ports of C++14/17 functions and type traits
|
||||
@ -67,6 +67,15 @@ namespace dlib
|
||||
using pop_front_t = typename pop_front<Sequence>::type;
|
||||
|
||||
// ---------------------------------------------------------------------
|
||||
|
||||
struct in_place_t
|
||||
{
|
||||
explicit in_place_t() = default;
|
||||
};
|
||||
|
||||
static constexpr in_place_t in_place{};
|
||||
|
||||
// ---------------------------------------------------------------------
|
||||
}
|
||||
|
||||
#endif //DLIB_UTILITY_Hh_
|
||||
|
Loading…
Reference in New Issue
Block a user