c++ - libro - singularidad filosofia
comprobar los parĂ¡metros de las plantillas variadic para la singularidad (3)
Otra solución de profundidad de instanciación O (logN). Todavía necesita una gran limpieza, comentarios, espacios de nombres, cambio de nombre y duplicación de código reducido.
Felicitaciones de nuevo a Xeo , cuya versión de profundidad de instanciación de O (logN) de gen_seq
esto (nuevamente) se basa.
#include <cstddef>
// using aliases for cleaner syntax
template<class T> using Invoke = typename T::type;
template<std::size_t...> struct seq{ using type = seq; };
template<class S1, class S2> struct concat;
template<std::size_t... I1, std::size_t... I2>
struct concat<seq<I1...>, seq<I2...>>
: seq<I1..., (sizeof...(I1)+I2)...>{};
template<class S1, class S2>
using Concat = Invoke<concat<S1, S2>>;
template<std::size_t N> struct gen_seq;
template<std::size_t N> using GenSeq = Invoke<gen_seq<N>>;
template<std::size_t N>
struct gen_seq : Concat<GenSeq<N/2>, GenSeq<N - N/2>>{};
template<> struct gen_seq<0> : seq<>{};
template<> struct gen_seq<1> : seq<0>{};
Aparte de la generación de la secuencia de índices, esta solución debe tener incluso O (1) profundidad de instanciación. En lugar de herencia múltiple, usa std::array<std::false_type, size>
para realizar O (1) -instantiation-depth O a través de SFINAE.
Implementación de is_one_of
. Tenga en cuenta que "es uno de" es el concepto opuesto de "son únicos".
#include <array>
// check if `T` is in `Us...`
template<class T, class... Us>
struct is_one_of
{
template<class T1>
static constexpr auto SFINAE(int)
-> decltype( std::array<std::false_type, sizeof...(Us)>
{{std::is_same<T1, Us>{}...}} )
{ return {}; /* only to suppress warning */ }
template<class...>
static constexpr int SFINAE(...) { return 42; }
template<class T1>
static constexpr bool test()
{
return std::is_same<decltype(SFINAE<T1>(0)), int>{};
}
static constexpr bool value = test<T>();
constexpr operator bool() const { return value; }
};
Implementación de are_unique
:
namespace detail
{
// `Any` type with a generic no-constraint ctor
// to discard a number of arguments for a function template
template<std::size_t>
struct Any
{
template<class T>
constexpr Any(T&&) {}
};
// `wrapper` is used as a substitute for `declval`,
// and can keep track if `T` is a reference
template<class T>
struct wrapper { using type = T; };
template<std::size_t I, class T, class... Us>
struct is_one_of_pack
{
template<std::size_t... I1s>
struct helper
{
template<class... Remaining>
static constexpr bool deduce_remaining(Any<I1s>..., Remaining...)
{
// unique <-> is one of
return not is_one_of<T, typename Remaining::type...>{};
}
};
template<std::size_t... I1s>
static constexpr bool deduce_seq(seq<I1s...>)
{
return helper<I1s...>::template deduce_remaining(wrapper<Us>()...);
}
static constexpr bool create_seq()
{
return deduce_seq(gen_seq<I+1>{});
}
using type = std::integral_constant<bool, create_seq()>;
};
template<class... Packs>
constexpr auto SFINAE(int)
-> decltype( std::array<std::true_type, sizeof...(Packs)>
{{typename Packs::type{}...}} )
{ return {}; /* only to suppress warning */ }
template<class...>
static constexpr int SFINAE(...) { return 42; }
template<class... Packs>
constexpr bool test()
{
return std::is_same<decltype(SFINAE<Packs...>(0)), int>{};
}
template<class... Ts, std::size_t... Is>
constexpr bool deduce_seq(seq<Is...>)
{
return test< is_one_of_pack<Is, Ts, Ts...>... >();
}
}
template<class... Ts>
struct are_unique
: std::integral_constant<bool,
detail::deduce_seq<Ts...>(gen_seq<sizeof...(Ts)>{})>
{};
Ejemplo de uso:
#include <iostream>
#include <iomanip>
int main()
{
bool a = are_unique<bool, char, int>();
bool b = are_unique<bool, char, int, bool>();
bool c = are_unique<bool, char, bool, int>();
std::cout << std::boolalpha;
std::cout << a << std::endl;
std::cout << b << std::endl;
std::cout << c << std::endl;
}
Quiero que los parámetros de la plantilla varíen únicos. Sé cuando herencia múltiple, herencia de clases idéntica no está permitida.
struct A{};
struct B: A, A{}; // error
Usando esta regla, hice un pequeño código.
#include <type_traits>
template< class T> struct id{};
template< class ...T> struct base_all : id<T> ... {};
template< class ... T>
struct is_unique
{
template< class ... U>
static constexpr bool test( base_all<U...> * ) noexcept { return true; }
template< class ... U>
static constexpr bool test( ... ) noexcept { return false;}
static constexpr bool value = test<T...>(0);
};
int main()
{
constexpr bool b = is_unique<int, float, double>::value; // false -- Why?
constexpr bool c = is_unique< int, char, int>::value; // false
static_assert( b == true && c == false , "!");// failed.
}
Pero mi programa no funciona como esperaba. ¿Qué pasa?
// ACTUALIZACIÓN: // Gracias, soluciono mi error: //
// #include <type_traits>
// #include <cstddef>
//
// template< class ... U> struct pack{};
//
// template< class T> struct id{};
// template< class T> struct base_all;
// template< class ... T> struct base_all< pack<T...> > : id<T> ... {};
//
//
//
// template< class ... T>
// struct is_unique
// {
// template< class P, std::size_t = sizeof(base_all<P>) >
// struct check;
//
// template< class ...U>
// static constexpr bool test(check< pack<U...> > * ) noexcept { return true;}
//
// template< class ... U>
// static constexpr bool test(...)noexcept { return false;}
//
// static constexpr bool value = test<T...>(0);
// };
//
// int main()
// {
// constexpr bool b = is_unique<int, float, double>::value; // true
// constexpr bool c = is_unique< int, char, int>::value; // false
//
// static_assert( b == true && c == false , "!");// success.
// }
//
P: alguien puede explicar, ¿por qué falló?
ACTUALIZACIÓN2: mi actualización anterior fue ilegal :)). Forma legal, pero compiló O (N) tiempo.
#include <cstddef>
#include <iostream>
#include <type_traits>
namespace mpl
{
template< class T > using invoke = typename T :: type ;
template< class C, class I, class E > using if_t = invoke< std::conditional< C{}, I, E> >;
template< class T > struct id{};
struct empty{};
template< class A, class B > struct base : A, B {};
template< class B , class ... > struct is_unique_impl;
template< class B > struct is_unique_impl<B>: std::true_type{};
template< class B, class T, class ... U>
struct is_unique_impl<B, T, U...> : if_t< std::is_base_of< id<T>, B>, std::false_type, is_unique_impl< base<B,id<T>>, U...> >{};
template< class ...T >struct is_unique : is_unique_impl< empty, T ... > {};
} // mpl
int main()
{
constexpr bool b = mpl::is_unique<int, float, double>::value;
constexpr bool c = mpl::is_unique< int, char, int > :: value;
static_assert( b == true , "!");
static_assert( c == false, "!");
return 0;
}
Pasar un puntero a base_all<U...>
simplemente requiere la existencia de una declaración de base_all<U...>
. Sin intentar acceder a la definición, el compilador no detectará que el tipo está realmente mal definido. Un enfoque para mitigar ese problema sería usar un argumento que requiera una definición de base_all<U...>
, por ejemplo:
template< class ...T> struct base_all
: id<T> ...
{
typedef int type;
};
// ...
template< class ... U>
static constexpr bool test(typename base_all<U...>::type) noexcept
{
return true;
}
Aunque lo anterior responde a la pregunta, no compila: la herencia múltiple creada no se encuentra en un contexto adecuado para ser considerada para SFINAE. No creo que pueda aprovechar la regla de no permitir que se herede la misma base dos veces. La prueba relevante se puede implementar de manera diferente, sin embargo:
#include <type_traits>
template <typename...>
struct is_one_of;
template <typename F>
struct is_one_of<F>
{
static constexpr bool value = false;
};
template <typename F, typename S, typename... T>
struct is_one_of<F, S, T...>
{
static constexpr bool value = std::is_same<F, S>::value
|| is_one_of<F, T...>::value;
};
template <typename...>
struct is_unique;
template <>
struct is_unique<> {
static constexpr bool value = true;
};
template<typename F, typename... T>
struct is_unique<F, T...>
{
static constexpr bool value = is_unique<T...>::value
&& !is_one_of<F, T...>::value;
};
int main()
{
constexpr bool b = is_unique<int, float, double>::value;
constexpr bool c = is_unique< int, char, int>::value;
static_assert( b == true && c == false , "!");
}
template<typename ... _Types>
class unique_types;
template<typename _T1, typename _T2, typename ... _Tail>
class unique_types<_T1,_T2,_Tail...> :
virtual public unique_types<_T1, _T2>
, virtual public unique_types<_T1, _Tail ...>
, virtual public unique_types<_T2, _Tail ...>
{
protected:
using check_current = unique_types<_T1, _T2>;
using check_first = unique_types<_T1, _Tail ...>;
using check_second = unique_types<_T2, _Tail ...>;
public:
constexpr static const bool value = check_current::value && check_first::value && check_second::value;
};
template<typename _T1, typename _T2>
class unique_types<_T1, _T2, _T2>
{
public:
constexpr static const bool value = false;
};
template<typename _T1, typename ... _Tail>
class unique_types<_T1, _T1, _Tail ...>
{
public:
constexpr static const bool value = false;
};
template<typename _T1, typename _T2>
class unique_types<_T1, _T2, _T1>
{
public:
constexpr static const bool value = false;
};
template<typename _T1, typename _T2>
class unique_types<_T1,_T2>
{
public:
constexpr static const bool value = true;
};
template<typename _T1>
class unique_types<_T1,_T1>
{
public:
constexpr static const bool value = false;
};
template<typename _T1>
class unique_types<_T1>
{
public:
constexpr static const bool value = true;
};
class A
{
public:
A() = default;
};
inline void test()
{
const bool unique = unique_types<int, short, float, A>::value;
assert(unique == true);
const bool unique2 = unique_types<int, A, short, float, A>::value;
assert(unique2 == false);
const bool unique3 = unique_types<A, int, short, float, A>::value;
assert(unique3 == false);
const bool unique4 = unique_types<int, short, A, float, A>::value;
assert(unique4 == false);
const bool unique5 = unique_types<int, short, float, A, A>::value;
assert(unique5 == false);
const bool unique6 = unique_types<int>::value;
assert(unique6 == true);
const bool unique7 = unique_types<int, int>::value;
assert(unique7 == false);
const bool unique8 = unique_types<int, int, char>::value;
assert(unique8 == false);
const bool unique9 = unique_types<int, char, int>::value;
assert(unique9 == false);
const bool unique10 = unique_types<char, int, int>::value;
assert(unique10 == false);
const bool unique11 = unique_types<int, int, A, char>::value;
assert(unique11 == false);
const bool unique12 = unique_types<int, A, char, int>::value;
assert(unique12 == false);
const bool unique13 = unique_types<A, char, int, int>::value;
assert(unique13 == false);
}