Info
-
Did you know that in C++
char
,signed char
andunsigned char
are 3 different types?
Example
static_assert(not std::is_same_v<char, signed char>);
static_assert(std::is_same_v<int, signed int>);
static_assert(not std::is_same_v<char, unsigned char>);
static_assert(not std::is_same_v<int, unsigned int>);
Puzzle
- Can you implement
unique_types
concept which is satisfied when all given types are unique and afoo
function which only allows to take unique arguments?
template<class... Ts>
/*TODO */ concept unique_types = false;
static_assert(unique_types<char>);
static_assert(unique_types<signed char>);
static_assert(unique_types<signed, char>);
static_assert(unique_types<unsigned char>);
static_assert(unique_types<char, signed char>);
static_assert(unique_types<signed char, char>);
static_assert(unique_types<char, signed char, unsigned char>);
static_assert(unique_types<std::byte, char, signed char, unsigned char>);
static_assert(not unique_types<signed char, signed char>);
static_assert(not unique_types<char, signed, char>);
static_assert(not unique_types<char, signed, char, char>);
static_assert(not unique_types<std::uint8_t, unsigned char>);
static_assert(not unique_types<std::uint8_t, unsigned char>);
static_assert(not unique_types<std::uint8_t, std::byte, char, signed char, unsigned char>);
static_assert(not unique_types<std::int8_t, std::uint8_t, std::byte, char, signed char, unsigned char>);
/*TODO*/ constexpr auto foo(...) { }
static_assert([](auto... args) { return requires { foo(args...); }; }(char{}));
static_assert([](auto... args) { return requires { foo(args...); }; }(char{}, signed{}));
static_assert([](auto... args) { return requires { foo(args...); }; }((signed char){}, char{}));
static_assert(not [](auto... args) { return requires { foo(args...); }; }(char{}, char{}));
static_assert(not [](auto... args) { return requires { foo(args...); }; }((signed char){}, char{}, (signed char){}));
Solutions
#include <type_traits>
#include <cstddef>
#include <cstdint>
#include <boost/mp11/list.hpp>
#include <boost/mp11/set.hpp>
template<class... Ts>
concept unique_types = boost::mp11::mp_is_set<boost::mp11::mp_list<Ts...>>::value;
static_assert(unique_types<char>);
static_assert(unique_types<signed char>);
static_assert(unique_types<signed, char>);
static_assert(unique_types<unsigned char>);
static_assert(unique_types<char, signed char>);
static_assert(unique_types<signed char, char>);
static_assert(unique_types<char, signed char, unsigned char>);
static_assert(unique_types<std::byte, char, signed char, unsigned char>);
static_assert(not unique_types<signed char, signed char>);
static_assert(not unique_types<char, signed, char>);
static_assert(not unique_types<char, signed, char, char>);
static_assert(not unique_types<std::uint8_t, unsigned char>);
static_assert(not unique_types<std::uint8_t, unsigned char>);
static_assert(not unique_types<std::uint8_t, std::byte, char, signed char, unsigned char>);
static_assert(not unique_types<std::int8_t, std::uint8_t, std::byte, char, signed char, unsigned char>);
template<class... Ts>
constexpr auto foo(Ts...) requires unique_types<Ts...> { }
static_assert([](auto... args) { return requires { foo(args...); }; }(char{}));
static_assert([](auto... args) { return requires { foo(args...); }; }(char{}, signed{}));
static_assert([](auto... args) { return requires { foo(args...); }; }((signed char){}, char{}));
static_assert(not [](auto... args) { return requires { foo(args...); }; }(char{}, char{}));
static_assert(not [](auto... args) { return requires { foo(args...); }; }((signed char){}, char{}, (signed char){}));
template <typename...> constexpr bool unique_head = true;
template <typename T, typename... Ts>
constexpr bool unique_head<T, Ts...> = ((not std::is_same_v<T, Ts>) and ...) and unique_head<Ts...>;
template <typename... Ts>
concept unique_types = unique_head<Ts...>;
static_assert(unique_types<char>);
static_assert(unique_types<signed char>);
static_assert(unique_types<signed, char>);
static_assert(unique_types<unsigned char>);
static_assert(unique_types<char, signed char>);
static_assert(unique_types<signed char, char>);
static_assert(unique_types<char, signed char, unsigned char>);
static_assert(unique_types<std::byte, char, signed char, unsigned char>);
static_assert(not unique_types<signed char, signed char>);
static_assert(not unique_types<char, signed, char>);
static_assert(not unique_types<char, signed, char, char>);
static_assert(not unique_types<signed, char, char>);
static_assert(not unique_types<std::uint8_t, unsigned char>);
static_assert(not unique_types<std::uint8_t, unsigned char>);
static_assert(not unique_types<std::uint8_t, std::byte, char, signed char, unsigned char>);
static_assert(not unique_types<std::int8_t, std::uint8_t, std::byte, char, signed char, unsigned char>);
template <typename... Ts> requires unique_types<Ts...>
constexpr auto foo(Ts&&...) { }
static_assert([](auto... args) { return requires { foo(args...); }; }(char{}));
static_assert([](auto... args) { return requires { foo(args...); }; }(char{}, signed{}));
static_assert([](auto... args) { return requires { foo(args...); }; }((signed char){}, char{}));
static_assert(not [](auto... args) { return requires { foo(args...); }; }(char{}, char{}));
static_assert(not [](auto... args) { return requires { foo(args...); }; }((signed char){}, char{}, (signed char){}));
template<class ...Ts> bool constexpr unique_types_impl = false;
template<class T1> bool constexpr unique_types_impl<T1> = true;
template<class T, class ...Ts>
bool constexpr unique_types_impl<T, Ts...> =
(!std::is_same_v<T, Ts> && ... ) && unique_types_impl<Ts...>;
template<class... Ts> concept unique_types = unique_types_impl<Ts...>;
static_assert(unique_types<char>);
static_assert(unique_types<signed char>);
static_assert(unique_types<signed, char>);
static_assert(unique_types<unsigned char>);
static_assert(unique_types<char, signed char>);
static_assert(unique_types<signed char, char>);
static_assert(unique_types<char, signed char, unsigned char>);
static_assert(unique_types<std::byte, char, signed char, unsigned char>);
static_assert(not unique_types<signed char, signed char>);
static_assert(not unique_types<char, signed, char>);
static_assert(not unique_types<char, signed, char, char>);
static_assert(not unique_types<std::uint8_t, unsigned char>);
static_assert(not unique_types<std::uint8_t, unsigned char>);
static_assert(not unique_types<std::uint8_t, std::byte, char, signed char, unsigned char>);
static_assert(not unique_types<std::int8_t, std::uint8_t, std::byte, char, signed char, unsigned char>);
template<class ...Ts>
constexpr auto foo(Ts...) requires unique_types<Ts...> {}
static_assert([](auto... args) { return requires { foo(args...); }; }(char{}));
static_assert([](auto... args) { return requires { foo(args...); }; }(char{}, signed{}));
static_assert([](auto... args) { return requires { foo(args...); }; }((signed char){}, char{}));
static_assert(not [](auto... args) { return requires { foo(args...); }; }(char{}, char{}));
static_assert(not [](auto... args) { return requires { foo(args...); }; }((signed char){}, char{}, (signed char){}));
template <typename T, typename... Ts>
constexpr bool are_unique() {
if constexpr (sizeof...(Ts) == 0)
return true;
else
return are_unique<Ts...>()
and not boost::mp11::mp_contains<boost::mp11::mp_list<Ts...>, T>::value;
}
template<class... Ts>
concept unique_types = are_unique<Ts...>();
static_assert(unique_types<char>);
static_assert(unique_types<signed char>);
static_assert(unique_types<signed, char>);
static_assert(unique_types<unsigned char>);
static_assert(unique_types<char, signed char>);
static_assert(unique_types<signed char, char>);
static_assert(unique_types<char, signed char, unsigned char>);
static_assert(unique_types<std::byte, char, signed char, unsigned char>);
static_assert(not unique_types<signed char, signed char>);
static_assert(not unique_types<char, signed, char>);
static_assert(not unique_types<char, signed, char, char>);
static_assert(not unique_types<std::uint8_t, unsigned char>);
static_assert(not unique_types<std::uint8_t, unsigned char>);
static_assert(not unique_types<std::uint8_t, std::byte, char, signed char, unsigned char>);
static_assert(not unique_types<std::int8_t, std::uint8_t, std::byte, char, signed char, unsigned char>);
template <typename... Ts>
constexpr auto foo(Ts&&...)
requires unique_types<Ts...>
{}
static_assert([](auto... args) { return requires { foo(args...); }; }(char{}));
static_assert([](auto... args) { return requires { foo(args...); }; }(char{}, signed{}));
static_assert([](auto... args) { return requires { foo(args...); }; }((signed char){}, char{}));
static_assert(not [](auto... args) { return requires { foo(args...); }; }(char{}, char{}));
static_assert(not [](auto... args) { return requires { foo(args...); }; }((signed char){}, char{}, (signed char){}));
template<class... Ts>
concept unique_types = std::is_same_v<mp_list<Ts...>, mp_unique<mp_list<Ts...>>>;
static_assert(unique_types<char>);
static_assert(unique_types<signed char>);
static_assert(unique_types<signed, char>);
static_assert(unique_types<unsigned char>);
static_assert(unique_types<char, signed char>);
static_assert(unique_types<signed char, char>);
static_assert(unique_types<char, signed char, unsigned char>);
static_assert(unique_types<std::byte, char, signed char, unsigned char>);
static_assert(not unique_types<signed char, signed char>);
static_assert(not unique_types<char, signed, char>);
static_assert(not unique_types<char, signed, char, char>);
static_assert(not unique_types<std::uint8_t, unsigned char>);
static_assert(not unique_types<std::uint8_t, unsigned char>);
static_assert(not unique_types<std::uint8_t, std::byte, char, signed char, unsigned char>);
static_assert(not unique_types<std::int8_t, std::uint8_t, std::byte, char, signed char, unsigned char>);
template<class... Ts> requires unique_types<Ts...>
constexpr auto foo(Ts...) { }
static_assert([](auto... args) { return requires { foo(args...); }; }(char{}));
static_assert([](auto... args) { return requires { foo(args...); }; }(char{}, signed{}));
static_assert([](auto... args) { return requires { foo(args...); }; }((signed char){}, char{}));
static_assert(not [](auto... args) { return requires { foo(args...); }; }(char{}, char{}));
static_assert(not [](auto... args) { return requires { foo(args...); }; }((signed char){}, char{}, (signed char){}));
template<class... Ts>
concept unique_types = std::is_same_v<mp_unique_if<mp_list<Ts...>, std::is_same>, mp_list<Ts...>>;
static_assert(unique_types<char>);
static_assert(unique_types<signed char>);
static_assert(unique_types<signed, char>);
static_assert(unique_types<unsigned char>);
static_assert(unique_types<char, signed char>);
static_assert(unique_types<signed char, char>);
static_assert(unique_types<char, signed char, unsigned char>);
static_assert(unique_types<std::byte, char, signed char, unsigned char>);
static_assert(not unique_types<signed char, signed char>);
static_assert(not unique_types<char, signed, char>);
static_assert(not unique_types<char, signed, char, char>);
static_assert(not unique_types<std::uint8_t, unsigned char>);
static_assert(not unique_types<std::uint8_t, unsigned char>);
static_assert(not unique_types<std::uint8_t, std::byte, char, signed char, unsigned char>);
static_assert(not unique_types<std::int8_t, std::uint8_t, std::byte, char, signed char, unsigned char>);
template<typename... T>
constexpr auto foo(T &&... t) requires unique_types<T...> { }
static_assert([](auto... args) { return requires { foo(args...); }; }(char{}));
static_assert([](auto... args) { return requires { foo(args...); }; }(char{}, signed{}));
static_assert([](auto... args) { return requires { foo(args...); }; }((signed char){}, char{}));
static_assert(not [](auto... args) { return requires { foo(args...); }; }(char{}, char{}));
static_assert(not [](auto... args) { return requires { foo(args...); }; }((signed char){}, char{}, (signed char){}));
template <typename...>
constexpr bool unique = true;
template <typename T, typename... Ts>
constexpr bool unique<T, Ts...> = (!std::is_same_v<T, Ts> && ...) && unique<Ts...>;
template<class... Ts>
concept unique_types = unique<Ts...>;
static_assert(unique_types<char>);
static_assert(unique_types<signed char>);
static_assert(unique_types<signed, char>);
static_assert(unique_types<unsigned char>);
static_assert(unique_types<char, signed char>);
static_assert(unique_types<signed char, char>);
static_assert(unique_types<char, signed char, unsigned char>);
static_assert(unique_types<std::byte, char, signed char, unsigned char>);
static_assert(not unique_types<signed char, signed char>);
static_assert(not unique_types<char, signed, char>);
static_assert(not unique_types<char, signed, char, char>);
static_assert(not unique_types<std::uint8_t, unsigned char>);
static_assert(not unique_types<std::uint8_t, unsigned char>);
static_assert(not unique_types<std::uint8_t, std::byte, char, signed char, unsigned char>);
static_assert(not unique_types<std::int8_t, std::uint8_t, std::byte, char, signed char, unsigned char>);
template< typename ...Ts>
constexpr auto foo(Ts...) requires unique_types<Ts...> {}
static_assert([](auto... args) { return requires { foo(args...); }; }(char{}));
static_assert([](auto... args) { return requires { foo(args...); }; }(char{}, signed{}));
static_assert([](auto... args) { return requires { foo(args...); }; }((signed char){}, char{}));
static_assert(not [](auto... args) { return requires { foo(args...); }; }(char{}, char{}));
static_assert(not [](auto... args) { return requires { foo(args...); }; }((signed char){}, char{}, (signed char){}));