mirror of
https://github.com/davisking/dlib.git
synced 2024-11-01 10:14:53 +08:00
if constexpr() for C++14 (#2654)
* - Use C++14 library stuff if possible * Added a C++14 of if constexpr(). I think it's pretty great. Very similar to hana::if_ * - added some example usages * - added is_detected - added more tests * - fix * - fix * - much better. thanks Davis * - fix * - fix * - fix * - Davis, I think you were wrong. Here, everything breaks without the _ function * - Fixed but docs suck * - use C++14 stuff * - put function body after docs * - docs maybe slightly better but still suck * - badly placed brace * - maybe slightly better English * - better API i think Co-authored-by: pf <pf@me>
This commit is contained in:
parent
902c70193a
commit
4db03ee6fd
138
dlib/constexpr_if.h
Normal file
138
dlib/constexpr_if.h
Normal file
@ -0,0 +1,138 @@
|
||||
// Copyright (C) 2022 Davis E. King (davis@dlib.net)
|
||||
// License: Boost Software License See LICENSE.txt for the full license.
|
||||
#ifndef DLIB_IF_CONSTEXPR_H
|
||||
#define DLIB_IF_CONSTEXPR_H
|
||||
|
||||
#include "overloaded.h"
|
||||
|
||||
namespace dlib
|
||||
{
|
||||
// ----------------------------------------------------------------------------------------
|
||||
|
||||
namespace detail
|
||||
{
|
||||
const auto _ = [](auto&& arg) -> decltype(auto) { return std::forward<decltype(arg)>(arg); };
|
||||
|
||||
template<typename...T>
|
||||
using void_t = void;
|
||||
|
||||
template<typename Void, template <class...> class Op, class... Args>
|
||||
struct is_detected : std::false_type{};
|
||||
|
||||
template<template <class...> class Op, class... Args>
|
||||
struct is_detected<void_t<Op<Args...>>, Op, Args...> : std::true_type {};
|
||||
}
|
||||
|
||||
// ----------------------------------------------------------------------------------------
|
||||
|
||||
template<typename... T>
|
||||
struct types_ {};
|
||||
/*!
|
||||
WHAT THIS OBJECT REPRESENTS
|
||||
This is a type list. Use this to pass types to the switch_() function as
|
||||
compile-time initial conditions.
|
||||
!*/
|
||||
|
||||
// ----------------------------------------------------------------------------------------
|
||||
|
||||
template<bool... v>
|
||||
auto bools(std::integral_constant<bool, v>...)
|
||||
/*!
|
||||
ensures
|
||||
- returns a type list of compile time booleans.
|
||||
!*/
|
||||
{
|
||||
return types_<std::integral_constant<bool,v>...>{};
|
||||
}
|
||||
|
||||
using true_t = types_<std::true_type>;
|
||||
using false_t = types_<std::false_type>;
|
||||
|
||||
// ----------------------------------------------------------------------------------------
|
||||
|
||||
template <
|
||||
typename... T,
|
||||
typename... Cases
|
||||
>
|
||||
constexpr decltype(auto) switch_(
|
||||
types_<T...> /*meta_obj*/,
|
||||
Cases&&... cases
|
||||
)
|
||||
/*!
|
||||
requires
|
||||
- meta_obj combines a set of initial types. These are used as compile-time initial conditions.
|
||||
- cases is a set of overload-able conditional branches.
|
||||
- at least one of the cases is callable given meta_obj.
|
||||
- each case statement has signature auto(types_<>..., auto _) where _ is an identity function
|
||||
with identical behaviour to std::identity. This is used to make each generic lambda artificially
|
||||
dependent on the function body. This allows semantic analysis of the lambdas to be performed AFTER
|
||||
the correct lambda is chosen depending on meta_obj. This is the crucial bit that makes switch_() behave
|
||||
in a similar way to "if constexpr()" in C++17. Make sure to use _ on one of the objects in the lambdas.
|
||||
ensures
|
||||
- calls the correct conditional branch.
|
||||
- the correct conditional branch is selected at compile-time.
|
||||
- Note, each branch can return different types, and the return type of the switch_() function
|
||||
is that of the compile-time selected branch.
|
||||
|
||||
Here is an example:
|
||||
|
||||
template<typename T>
|
||||
auto perform_correct_action(T& obj)
|
||||
{
|
||||
return switch(
|
||||
types_<T>{},
|
||||
[&](types_<A>, auto _) {
|
||||
return _(obj).set_something_specific_to_A_and_return_something();
|
||||
},
|
||||
[&](types_<B>, auto _) {
|
||||
return _(obj).set_something_specific_to_B_and_return_something();
|
||||
},
|
||||
[&](auto...) {
|
||||
// Default case statement. Do something sensible.
|
||||
return false;
|
||||
}
|
||||
);
|
||||
}
|
||||
|
||||
Here is another example:
|
||||
|
||||
template<typename T>
|
||||
auto transfer_state(T& a, T& b)
|
||||
{
|
||||
return switch(
|
||||
bools(std::is_move_constructible<T>{}, std::is_copy_constructible<T>{}),
|
||||
[&](true_t, auto, auto _) {
|
||||
// T is both move-constructible. Copy semantics can be anything
|
||||
a = std::move(_(b));
|
||||
return move_tag{}; // Just for fun, we return different types in each branch.
|
||||
},
|
||||
[&](auto, true_t, auto _) {
|
||||
// T is copy-constructible, Move semantics can be anything. Though in this case,
|
||||
// if it had been move-constructible, the first branch would have been selected.
|
||||
// So in this case, it is not move-constructible.
|
||||
a = _(b);
|
||||
return copy_tag{};
|
||||
},
|
||||
[&](auto...) {
|
||||
// Default case statement
|
||||
return dont_care_tag{};
|
||||
}
|
||||
);
|
||||
}
|
||||
!*/
|
||||
{
|
||||
return overloaded(std::forward<Cases>(cases)...)(types_<T>{}..., detail::_);
|
||||
}
|
||||
// ----------------------------------------------------------------------------------------
|
||||
|
||||
template<template <class...> class Op, class... Args>
|
||||
using is_detected = detail::is_detected<void, Op, Args...>;
|
||||
/*!
|
||||
ensures
|
||||
- This is exactly the same as std::experimental::is_detected from library fundamentals v
|
||||
!*/
|
||||
|
||||
// ----------------------------------------------------------------------------------------
|
||||
}
|
||||
|
||||
#endif //DLIB_IF_CONSTEXPR_H
|
115
dlib/overloaded.h
Normal file
115
dlib/overloaded.h
Normal file
@ -0,0 +1,115 @@
|
||||
// Copyright (C) 2022 Davis E. King (davis@dlib.net)
|
||||
// License: Boost Software License See LICENSE.txt for the full license.
|
||||
#ifndef DLIB_OVERLOADED_H_
|
||||
#define DLIB_OVERLOADED_H_
|
||||
|
||||
#include <utility>
|
||||
#include <type_traits>
|
||||
|
||||
namespace dlib
|
||||
{
|
||||
namespace detail
|
||||
{
|
||||
#if __cpp_fold_expressions
|
||||
template<typename ...Base>
|
||||
struct overloaded_helper : Base...
|
||||
{
|
||||
template<typename... T>
|
||||
overloaded_helper(T&& ... t) : Base{std::forward<T>(t)}... {}
|
||||
|
||||
using Base::operator()...;
|
||||
};
|
||||
#else
|
||||
template<typename Base, typename ... BaseRest>
|
||||
struct overloaded_helper: Base, overloaded_helper<BaseRest...>
|
||||
{
|
||||
template<typename T, typename ... TRest>
|
||||
overloaded_helper(T&& t, TRest&& ...trest) :
|
||||
Base{std::forward<T>(t)},
|
||||
overloaded_helper<BaseRest...>{std::forward<TRest>(trest)...}
|
||||
{}
|
||||
|
||||
using Base::operator();
|
||||
using overloaded_helper<BaseRest...>::operator();
|
||||
};
|
||||
|
||||
template<typename Base>
|
||||
struct overloaded_helper<Base> : Base
|
||||
{
|
||||
template<typename T>
|
||||
overloaded_helper<Base>(T&& t) : Base{std::forward<T>(t)}
|
||||
{}
|
||||
|
||||
using Base::operator();
|
||||
};
|
||||
#endif //__cpp_fold_expressions
|
||||
}
|
||||
template<typename... T>
|
||||
auto overloaded(T&&... t)
|
||||
/*!
|
||||
This is a helper function for combining many callable objects (usually lambdas), into
|
||||
an overload-able set. This can be used in visitor patterns like
|
||||
- dlib::type_safe_union::apply_to_contents()
|
||||
- dlib::visit()
|
||||
- dlib::for_each()
|
||||
- dlib::switch_()
|
||||
|
||||
A picture paints a thousand words:
|
||||
|
||||
using tsu = type_safe_union<int,float,std::string>;
|
||||
|
||||
tsu a = std::string("hello there");
|
||||
|
||||
std::string result;
|
||||
|
||||
a.apply_to_contents(overloaded(
|
||||
[&result](int) {
|
||||
result = std::string("int");
|
||||
},
|
||||
[&result](float) {
|
||||
result = std::string("float");
|
||||
},
|
||||
[&result](const std::string& item) {
|
||||
result = item;
|
||||
}
|
||||
));
|
||||
|
||||
assert(result == "hello there");
|
||||
result = "";
|
||||
|
||||
result = visit(overloaded(
|
||||
[](int) {
|
||||
return std::string("int");
|
||||
},
|
||||
[](float) {
|
||||
return std::string("float");
|
||||
},
|
||||
[](const std::string& item) {
|
||||
return item;
|
||||
}
|
||||
), a);
|
||||
|
||||
assert(result == "hello there");
|
||||
|
||||
std::vector<int> type_ids;
|
||||
|
||||
for_each_type(a, overloaded(
|
||||
[&type_ids](in_place_tag<int>, tsu& me) {
|
||||
type_ids.push_back(me.get_type_id<int>());
|
||||
},
|
||||
[&type_ids](in_place_tag<float>, tsu& me) {
|
||||
type_ids.push_back(me.get_type_id<float>());
|
||||
},
|
||||
[&type_ids](in_place_tag<std::string>, tsu& me) {
|
||||
type_ids.push_back(me.get_type_id<std::string>());
|
||||
}
|
||||
));
|
||||
|
||||
assert(type_ids == vector<int>({0,1,2}));
|
||||
!*/
|
||||
{
|
||||
return detail::overloaded_helper<std::decay_t<T>...>{std::forward<T>(t)...};
|
||||
}
|
||||
}
|
||||
|
||||
#endif //DLIB_OVERLOADED_H_
|
@ -42,6 +42,7 @@ set (tests
|
||||
conditioning_class_c.cpp
|
||||
conditioning_class.cpp
|
||||
config_reader.cpp
|
||||
constexpr_if.cpp
|
||||
correlation_tracker.cpp
|
||||
crc32.cpp
|
||||
create_iris_datafile.cpp
|
||||
|
206
dlib/test/constexpr_if.cpp
Normal file
206
dlib/test/constexpr_if.cpp
Normal file
@ -0,0 +1,206 @@
|
||||
// Copyright (C) 2022 Davis E. King (davis@dlib.net)
|
||||
// License: Boost Software License See LICENSE.txt for the full license.
|
||||
|
||||
#include <string>
|
||||
#include <dlib/constexpr_if.h>
|
||||
#include <dlib/invoke.h>
|
||||
#include "tester.h"
|
||||
|
||||
namespace
|
||||
{
|
||||
using namespace test;
|
||||
using namespace dlib;
|
||||
|
||||
logger dlog("test.constexpr_if");
|
||||
|
||||
struct A
|
||||
{
|
||||
int i;
|
||||
};
|
||||
|
||||
struct B
|
||||
{
|
||||
float f;
|
||||
};
|
||||
|
||||
struct C
|
||||
{
|
||||
std::string str;
|
||||
};
|
||||
|
||||
struct D
|
||||
{
|
||||
int i;
|
||||
void set_i(int j) {i = j;}
|
||||
};
|
||||
|
||||
template<typename T>
|
||||
auto handle_type_and_return1(T obj)
|
||||
{
|
||||
return switch_(types_<T>{},
|
||||
[&](types_<A>, auto _) {
|
||||
return _(obj).i;
|
||||
},
|
||||
[&](types_<B>, auto _) {
|
||||
return _(obj).f;
|
||||
},
|
||||
[&](types_<C>, auto _) {
|
||||
return _(obj).str;
|
||||
},
|
||||
[&](auto...) {
|
||||
printf("Don't know what this type is\n");
|
||||
}
|
||||
);
|
||||
}
|
||||
|
||||
template<typename T>
|
||||
auto handle_type_and_return2(T obj)
|
||||
{
|
||||
return switch_(bools(std::is_same<T,A>{}, std::is_same<T,B>{}, std::is_same<T,C>{}),
|
||||
[&](true_t, auto, auto, auto _) {
|
||||
return _(obj).i;
|
||||
},
|
||||
[&](auto, true_t, auto, auto _) {
|
||||
return _(obj).f;
|
||||
},
|
||||
[&](auto, auto, true_t, auto _) {
|
||||
return _(obj).str;
|
||||
},
|
||||
[&](auto...) {
|
||||
printf("Don't know what this type is\n");
|
||||
}
|
||||
);
|
||||
}
|
||||
|
||||
void test_switch_type()
|
||||
{
|
||||
A a{1};
|
||||
B b{2.5f};
|
||||
C c{"hello there!"};
|
||||
|
||||
{
|
||||
auto ret = handle_type_and_return1(a);
|
||||
static_assert(std::is_same<decltype(ret), int>::value, "failed test");
|
||||
DLIB_TEST(ret == a.i);
|
||||
}
|
||||
|
||||
{
|
||||
auto ret = handle_type_and_return2(a);
|
||||
static_assert(std::is_same<decltype(ret), int>::value, "failed test");
|
||||
DLIB_TEST(ret == a.i);
|
||||
}
|
||||
|
||||
{
|
||||
auto ret = handle_type_and_return1(b);
|
||||
static_assert(std::is_same<decltype(ret), float>::value, "failed test");
|
||||
DLIB_TEST(ret == b.f);
|
||||
}
|
||||
|
||||
{
|
||||
auto ret = handle_type_and_return2(b);
|
||||
static_assert(std::is_same<decltype(ret), float>::value, "failed test");
|
||||
DLIB_TEST(ret == b.f);
|
||||
}
|
||||
|
||||
{
|
||||
auto ret = handle_type_and_return1(c);
|
||||
static_assert(std::is_same<decltype(ret), std::string>::value, "failed test");
|
||||
DLIB_TEST(ret == c.str);
|
||||
}
|
||||
|
||||
{
|
||||
auto ret = handle_type_and_return2(c);
|
||||
static_assert(std::is_same<decltype(ret), std::string>::value, "failed test");
|
||||
DLIB_TEST(ret == c.str);
|
||||
}
|
||||
}
|
||||
|
||||
template <typename Func, typename... Args>
|
||||
bool try_invoke(Func&& f, Args&&... args)
|
||||
{
|
||||
return switch_(bools(is_invocable<Func, Args...>{}),
|
||||
[&](true_t, auto _) {
|
||||
_(std::forward<Func>(f))(std::forward<Args>(args)...);
|
||||
return true;
|
||||
},
|
||||
[](auto...) {
|
||||
return false;
|
||||
}
|
||||
);
|
||||
}
|
||||
|
||||
void test_try_invoke()
|
||||
{
|
||||
int value = 0;
|
||||
|
||||
auto foo = [&]{ value++; };
|
||||
auto bar = [&](int i) { value += i; };
|
||||
auto baz = [&](int i, int j) { value += (i+j); };
|
||||
|
||||
DLIB_TEST(try_invoke(foo));
|
||||
DLIB_TEST(value == 1);
|
||||
DLIB_TEST(!try_invoke(foo, 1));
|
||||
DLIB_TEST(value == 1);
|
||||
DLIB_TEST(!try_invoke(foo, 1, 2));
|
||||
DLIB_TEST(value == 1);
|
||||
|
||||
DLIB_TEST(!try_invoke(bar));
|
||||
DLIB_TEST(value == 1);
|
||||
DLIB_TEST(try_invoke(bar, 1));
|
||||
DLIB_TEST(value == 2);
|
||||
DLIB_TEST(!try_invoke(bar, 1, 2));
|
||||
DLIB_TEST(value == 2);
|
||||
|
||||
DLIB_TEST(!try_invoke(baz));
|
||||
DLIB_TEST(value == 2);
|
||||
DLIB_TEST(!try_invoke(baz, 1));
|
||||
DLIB_TEST(value == 2);
|
||||
DLIB_TEST(try_invoke(baz, 1, 2));
|
||||
DLIB_TEST(value == 5);
|
||||
}
|
||||
|
||||
template<typename T>
|
||||
using set_i_pred = decltype(std::declval<T>().set_i(int{}));
|
||||
|
||||
template<typename T>
|
||||
bool try_set_i(T& obj, int i)
|
||||
{
|
||||
return switch_(bools(is_detected<set_i_pred, T>{}),
|
||||
[&](true_t, auto _) {
|
||||
_(obj).set_i(i);
|
||||
return true;
|
||||
},
|
||||
[](auto...){
|
||||
return false;
|
||||
}
|
||||
);
|
||||
}
|
||||
|
||||
void test_set_i()
|
||||
{
|
||||
A a{1};
|
||||
D d{1};
|
||||
DLIB_TEST(!try_set_i(a, 2));
|
||||
DLIB_TEST(a.i == 1);
|
||||
DLIB_TEST(try_set_i(d, 2));
|
||||
DLIB_TEST(d.i == 2);
|
||||
}
|
||||
|
||||
class constexpr_if_test : public tester
|
||||
{
|
||||
public:
|
||||
constexpr_if_test (
|
||||
) : tester ("test_constexpr_if",
|
||||
"Runs tests on the C++14 approximation of C++17 if constexpr() statements but better.")
|
||||
{}
|
||||
|
||||
void perform_test (
|
||||
)
|
||||
{
|
||||
test_switch_type();
|
||||
test_try_invoke();
|
||||
test_set_i();
|
||||
}
|
||||
} a;
|
||||
|
||||
}
|
@ -10,6 +10,7 @@
|
||||
#include <functional>
|
||||
#include "../serialize.h"
|
||||
#include "../utility.h"
|
||||
#include "../overloaded.h"
|
||||
|
||||
namespace dlib
|
||||
{
|
||||
@ -525,7 +526,7 @@ namespace dlib
|
||||
void for_each_type_impl(
|
||||
F&& f,
|
||||
TSU&& tsu,
|
||||
dlib::index_sequence<I...>
|
||||
std::index_sequence<I...>
|
||||
)
|
||||
{
|
||||
using Tsu = typename std::decay<TSU>::type;
|
||||
@ -550,7 +551,7 @@ namespace dlib
|
||||
{
|
||||
using Tsu = typename std::decay<TSU>::type;
|
||||
static constexpr std::size_t Size = type_safe_union_size<Tsu>::value;
|
||||
detail::for_each_type_impl(std::forward<F>(f), std::forward<TSU>(tsu), dlib::make_index_sequence<Size>{});
|
||||
detail::for_each_type_impl(std::forward<F>(f), std::forward<TSU>(tsu), std::make_index_sequence<Size>{});
|
||||
}
|
||||
|
||||
template<typename F, typename TSU>
|
||||
@ -638,50 +639,6 @@ namespace dlib
|
||||
throw serialization_error(e.info + "\n while deserializing an object of type type_safe_union");
|
||||
}
|
||||
}
|
||||
|
||||
#if __cplusplus >= 201703L
|
||||
|
||||
template<typename ...Base>
|
||||
struct overloaded_helper : Base...
|
||||
{
|
||||
template<typename... T>
|
||||
overloaded_helper(T&& ... t) : Base{std::forward<T>(t)}... {}
|
||||
|
||||
using Base::operator()...;
|
||||
};
|
||||
|
||||
#else
|
||||
|
||||
template<typename Base, typename ... BaseRest>
|
||||
struct overloaded_helper: Base, overloaded_helper<BaseRest...>
|
||||
{
|
||||
template<typename T, typename ... TRest>
|
||||
overloaded_helper(T&& t, TRest&& ...trest) :
|
||||
Base{std::forward<T>(t)},
|
||||
overloaded_helper<BaseRest...>{std::forward<TRest>(trest)...}
|
||||
{}
|
||||
|
||||
using Base::operator();
|
||||
using overloaded_helper<BaseRest...>::operator();
|
||||
};
|
||||
|
||||
template<typename Base>
|
||||
struct overloaded_helper<Base> : Base
|
||||
{
|
||||
template<typename T>
|
||||
overloaded_helper<Base>(T&& t) : Base{std::forward<T>(t)}
|
||||
{}
|
||||
|
||||
using Base::operator();
|
||||
};
|
||||
|
||||
#endif //__cplusplus >= 201703L
|
||||
|
||||
template<typename... T>
|
||||
overloaded_helper<typename std::decay<T>::type...> overloaded(T&&... t)
|
||||
{
|
||||
return overloaded_helper<typename std::decay<T>::type...>{std::forward<T>(t)...};
|
||||
}
|
||||
}
|
||||
|
||||
#endif // DLIB_TYPE_SAFE_UNIOn_h_
|
||||
|
@ -446,68 +446,6 @@ namespace dlib
|
||||
!*/
|
||||
|
||||
// ----------------------------------------------------------------------------------------
|
||||
|
||||
template<typename... T>
|
||||
overloaded_helper<typename std::decay<T>::type...> overloaded(T&&... t)
|
||||
{
|
||||
return overloaded_helper<typename std::decay<T>::type...>{std::forward<T>(t)...};
|
||||
}
|
||||
/*!
|
||||
This is a helper function for passing many callable objects (usually lambdas)
|
||||
to either apply_to_contents(), visit() or for_each(), that combine to make a complete
|
||||
visitor. A picture paints a thousand words:
|
||||
|
||||
using tsu = type_safe_union<int,float,std::string>;
|
||||
|
||||
tsu a = std::string("hello there");
|
||||
|
||||
std::string result;
|
||||
|
||||
a.apply_to_contents(overloaded(
|
||||
[&result](int) {
|
||||
result = std::string("int");
|
||||
},
|
||||
[&result](float) {
|
||||
result = std::string("float");
|
||||
},
|
||||
[&result](const std::string& item) {
|
||||
result = item;
|
||||
}
|
||||
));
|
||||
|
||||
assert(result == "hello there");
|
||||
result = "";
|
||||
|
||||
result = visit(overloaded(
|
||||
[](int) {
|
||||
return std::string("int");
|
||||
},
|
||||
[](float) {
|
||||
return std::string("float");
|
||||
},
|
||||
[](const std::string& item) {
|
||||
return item;
|
||||
}
|
||||
), a);
|
||||
|
||||
assert(result == "hello there");
|
||||
|
||||
std::vector<int> type_ids;
|
||||
|
||||
for_each_type(a, overloaded(
|
||||
[&type_ids](in_place_tag<int>, tsu& me) {
|
||||
type_ids.push_back(me.get_type_id<int>());
|
||||
},
|
||||
[&type_ids](in_place_tag<float>, tsu& me) {
|
||||
type_ids.push_back(me.get_type_id<float>());
|
||||
},
|
||||
[&type_ids](in_place_tag<std::string>, tsu& me) {
|
||||
type_ids.push_back(me.get_type_id<std::string>());
|
||||
}
|
||||
));
|
||||
|
||||
assert(type_ids == vector<int>({0,1,2}));
|
||||
!*/
|
||||
}
|
||||
|
||||
#endif // DLIB_TYPE_SAFE_UNION_KERNEl_ABSTRACT_
|
||||
|
@ -12,8 +12,17 @@
|
||||
|
||||
namespace dlib
|
||||
{
|
||||
// ---------------------------------------------------------------------
|
||||
#ifdef __cpp_lib_integer_sequence
|
||||
template<std::size_t... Ints>
|
||||
using index_sequence = std::index_sequence<Ints...>;
|
||||
|
||||
template<std::size_t N>
|
||||
using make_index_sequence = std::make_index_sequence<N>;
|
||||
|
||||
template<class... T>
|
||||
using index_sequence_for = std::index_sequence_for<T...>;
|
||||
#else
|
||||
// ---------------------------------------------------------------------
|
||||
template<std::size_t... Ints>
|
||||
struct index_sequence
|
||||
{
|
||||
@ -31,54 +40,44 @@ namespace dlib
|
||||
|
||||
template<std::size_t N>
|
||||
struct make_index_sequence
|
||||
: merge_and_renumber < typename make_index_sequence < N / 2 >::type,
|
||||
typename make_index_sequence < N - N / 2 >::type > {};
|
||||
: merge_and_renumber < typename make_index_sequence < N / 2 >::type,
|
||||
typename make_index_sequence < N - N / 2 >::type > {};
|
||||
|
||||
template<> struct make_index_sequence<0> : index_sequence<> {};
|
||||
template<> struct make_index_sequence<1> : index_sequence<0> {};
|
||||
|
||||
template<typename... Ts>
|
||||
using index_sequence_for = make_index_sequence<sizeof...(Ts)>;
|
||||
// ---------------------------------------------------------------------
|
||||
#endif
|
||||
|
||||
// ---------------------------------------------------------------------
|
||||
|
||||
template <typename First, typename... Rest>
|
||||
struct are_nothrow_move_constructible
|
||||
: std::integral_constant<bool, std::is_nothrow_move_constructible<First>::value &&
|
||||
are_nothrow_move_constructible<Rest...>::value> {};
|
||||
template<bool First, bool... Rest>
|
||||
struct And : std::integral_constant<bool, First && And<Rest...>::value> {};
|
||||
|
||||
template <typename T>
|
||||
struct are_nothrow_move_constructible<T> : std::is_nothrow_move_constructible<T> {};
|
||||
template<bool Value>
|
||||
struct And<Value> : std::integral_constant<bool, Value>{};
|
||||
|
||||
// ---------------------------------------------------------------------
|
||||
|
||||
template <typename First, typename... Rest>
|
||||
struct are_nothrow_move_assignable
|
||||
: std::integral_constant<bool, std::is_nothrow_move_assignable<First>::value &&
|
||||
are_nothrow_move_assignable<Rest...>::value> {};
|
||||
|
||||
template <typename T>
|
||||
struct are_nothrow_move_assignable<T> : std::is_nothrow_move_assignable<T> {};
|
||||
template <typename ...Types>
|
||||
struct are_nothrow_move_constructible : And<std::is_nothrow_move_constructible<Types>::value...> {};
|
||||
|
||||
// ---------------------------------------------------------------------
|
||||
|
||||
template <typename First, typename... Rest>
|
||||
struct are_nothrow_copy_constructible
|
||||
: std::integral_constant<bool, std::is_nothrow_copy_constructible<First>::value &&
|
||||
are_nothrow_copy_constructible<Rest...>::value> {};
|
||||
|
||||
template <typename T>
|
||||
struct are_nothrow_copy_constructible<T> : std::is_nothrow_copy_constructible<T> {};
|
||||
template <typename ...Types>
|
||||
struct are_nothrow_move_assignable : And<std::is_nothrow_move_assignable<Types>::value...> {};
|
||||
|
||||
// ---------------------------------------------------------------------
|
||||
|
||||
template <typename First, typename... Rest>
|
||||
struct are_nothrow_copy_assignable
|
||||
: std::integral_constant<bool, std::is_nothrow_copy_assignable<First>::value &&
|
||||
are_nothrow_copy_assignable<Rest...>::value> {};
|
||||
template <typename ...Types>
|
||||
struct are_nothrow_copy_constructible : And<std::is_nothrow_copy_constructible<Types>::value...> {};
|
||||
|
||||
template <typename T>
|
||||
struct are_nothrow_copy_assignable<T> : std::is_nothrow_copy_assignable<T> {};
|
||||
// ---------------------------------------------------------------------
|
||||
|
||||
template <typename ...Types>
|
||||
struct are_nothrow_copy_assignable : And<std::is_nothrow_copy_assignable<Types>::value...> {};
|
||||
|
||||
// ---------------------------------------------------------------------
|
||||
|
||||
@ -115,21 +114,8 @@ namespace dlib
|
||||
|
||||
// ---------------------------------------------------------------------
|
||||
|
||||
template <typename First, typename... Rest>
|
||||
struct are_nothrow_swappable
|
||||
: std::integral_constant<bool, is_nothrow_swappable<First>::value &&
|
||||
are_nothrow_swappable<Rest...>::value> {};
|
||||
|
||||
template <typename T>
|
||||
struct are_nothrow_swappable<T> : is_nothrow_swappable<T> {};
|
||||
|
||||
// ---------------------------------------------------------------------
|
||||
|
||||
template<bool First, bool... Rest>
|
||||
struct And : std::integral_constant<bool, First && And<Rest...>::value> {};
|
||||
|
||||
template<bool Value>
|
||||
struct And<Value> : std::integral_constant<bool, Value>{};
|
||||
template <typename ...Types>
|
||||
struct are_nothrow_swappable : And<is_nothrow_swappable<Types>::value...> {};
|
||||
|
||||
// ---------------------------------------------------------------------
|
||||
|
||||
|
Loading…
Reference in New Issue
Block a user