controlla i parametri dei modelli variadici per l’unicità

Voglio che i parametri del modello variadico debbano essere univoci. So che quando l’eredità multipla, l’ereditarietà delle classi identiche non è consentita.

struct A{}; struct B: A, A{}; // error 

Usando questa regola, ho creato un piccolo codice.

 #include  template struct id{}; template struct base_all : id ... {}; template struct is_unique { template static constexpr bool test( base_all * ) noexcept { return true; } template static constexpr bool test( ... ) noexcept { return false;} static constexpr bool value = test(0); }; int main() { constexpr bool b = is_unique::value; // false -- Why? constexpr bool c = is_unique::value; // false static_assert( b == true && c == false , "!");// failed. } 

Ma il mio programma non ha funzionato come mi aspettavo. Cosa c’è che non va?

// AGGIORNAMENTO: // Grazie, ho corretto il mio errore: //

 // #include  // #include  // // template struct pack{}; // // template struct id{}; // template struct base_all; // template struct base_all< pack > : id ... {}; // // // // template // struct is_unique // { // template< class P, std::size_t = sizeof(base_all

) > // struct check; // // template // static constexpr bool test(check< pack > * ) noexcept { return true;} // // template // static constexpr bool test(...)noexcept { return false;} // // static constexpr bool value = test(0); // }; // // int main() // { // constexpr bool b = is_unique::value; // true // constexpr bool c = is_unique::value; // false // // static_assert( b == true && c == false , "!");// success. // } //

D: qualcuno può spiegare, perché è fallito?

UPDATE2: il mio precedente aggiornamento era illegale :)). Forma legale, ma ha compilato il tempo O (N).

 #include  #include  #include  namespace mpl { template using invoke = typename T :: type ; template using if_t = invoke< std::conditional >; template struct id{}; struct empty{}; template struct base : A, B {}; template struct is_unique_impl; template struct is_unique_impl: std::true_type{}; template struct is_unique_impl : if_t< std::is_base_of< id, B>, std::false_type, is_unique_impl< base<B,id>, U...> >{}; templatestruct is_unique : is_unique_impl {}; } // mpl int main() { constexpr bool b = mpl::is_unique::value; constexpr bool c = mpl::is_unique :: value; static_assert( b == true , "!"); static_assert( c == false, "!"); return 0; } 

Passare un puntatore a base_all richiede semplicemente l’esistenza di una dichiarazione di base_all . Senza tentare di accedere alla definizione, il compilatore non rileverà che il tipo è effettivamente mal definito. Un approccio per mitigare tale problema sarebbe utilizzare un argomento che richiede una definizione di base_all , ad esempio:

 template< class ...T> struct base_all : id ... { typedef int type; }; // ... template< class ... U> static constexpr bool test(typename base_all::type) noexcept { return true; } 

Sebbene quanto sopra risponda alla domanda, non riesce a compilare: l’eredità multipla creata non è in un contesto adatto da considerare per SFINAE. Non penso che tu possa sfruttare la regola per non permettere la stessa base ereditata da due volte. Il test pertinente può essere implementato in modo diverso, tuttavia:

 #include  template  struct is_one_of; template  struct is_one_of { static constexpr bool value = false; }; template  struct is_one_of { static constexpr bool value = std::is_same::value || is_one_of::value; }; template  struct is_unique; template <> struct is_unique<> { static constexpr bool value = true; }; template struct is_unique { static constexpr bool value = is_unique::value && !is_one_of::value; }; int main() { constexpr bool b = is_unique::value; constexpr bool c = is_unique< int, char, int>::value; static_assert( b == true && c == false , "!"); } 

Un’altra soluzione di profondità di istanziazione O (logN). Ha ancora bisogno di una pulizia approfondita, commenti, spazi dei nomi, ridenominazione e riduzione della duplicazione del codice.

Ancora Kudos a Xeo , la cui versione di profondità di istanza di O (logN) di gen_seq si basa su questo (di nuovo).

 #include  // using aliases for cleaner syntax template using Invoke = typename T::type; template struct seq{ using type = seq; }; template struct concat; template struct concat, seq> : seq{}; template using Concat = Invoke>; template struct gen_seq; template using GenSeq = Invoke>; template struct gen_seq : Concat, GenSeq>{}; template<> struct gen_seq<0> : seq<>{}; template<> struct gen_seq<1> : seq<0>{}; 

A parte la generazione della sequenza degli indici, questa soluzione dovrebbe avere anche una profondità di istanziazione di O (1). Invece di ereditarietà multipla, usa uno std::array per fare una O (1) -stensione di approfondimento O via SFINAE.

Implementazione di is_one_of . Si noti che “è uno di” è il concetto opposto di “sono unici”.

 #include  // check if `T` is in `Us...` template struct is_one_of { template static constexpr auto SFINAE(int) -> decltype( std::array {{std::is_same{}...}} ) { return {}; /* only to suppress warning */ } template static constexpr int SFINAE(...) { return 42; } template static constexpr bool test() { return std::is_same(0)), int>{}; } static constexpr bool value = test(); constexpr operator bool() const { return value; } }; 

Implementazione di are_unique :

 namespace detail { // `Any` type with a generic no-constraint ctor // to discard a number of arguments for a function template template struct Any { template constexpr Any(T&&) {} }; // `wrapper` is used as a substitute for `declval`, // and can keep track if `T` is a reference template struct wrapper { using type = T; }; template struct is_one_of_pack { template struct helper { template static constexpr bool deduce_remaining(Any..., Remaining...) { // unique < -> is one of return not is_one_of{}; } }; template static constexpr bool deduce_seq(seq) { return helper::template deduce_remaining(wrapper()...); } static constexpr bool create_seq() { return deduce_seq(gen_seq{}); } using type = std::integral_constant; }; template constexpr auto SFINAE(int) -> decltype( std::array {{typename Packs::type{}...}} ) { return {}; /* only to suppress warning */ } template static constexpr int SFINAE(...) { return 42; } template constexpr bool test() { return std::is_same(0)), int>{}; } template constexpr bool deduce_seq(seq) { return test< is_one_of_pack... >(); } } template struct are_unique : std::integral_constant(gen_seq{})> {}; 

Esempio di utilizzo:

 #include  #include  int main() { bool a = are_unique(); bool b = are_unique(); bool c = are_unique(); std::cout < < std::boolalpha; std::cout << a << std::endl; std::cout << b << std::endl; std::cout << c << std::endl; } 
 template class unique_types; template 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 class unique_types<_t1 , _T2, _T2> { public: constexpr static const bool value = false; }; template class unique_types<_t1 , _T1, _Tail ...> { public: constexpr static const bool value = false; }; template class unique_types<_t1 , _T2, _T1> { public: constexpr static const bool value = false; }; template class unique_types<_t1 ,_T2> { public: constexpr static const bool value = true; }; template class unique_types<_t1 ,_T1> { public: constexpr static const bool value = false; }; template class unique_types<_t1> { public: constexpr static const bool value = true; }; class A { public: A() = default; }; inline void test() { const bool unique = unique_types::value; assert(unique == true); const bool unique2 = unique_types::value; assert(unique2 == false); const bool unique3 = unique_types::value; assert(unique3 == false); const bool unique4 = unique_types::value; assert(unique4 == false); const bool unique5 = unique_types::value; assert(unique5 == false); const bool unique6 = unique_types::value; assert(unique6 == true); const bool unique7 = unique_types::value; assert(unique7 == false); const bool unique8 = unique_types::value; assert(unique8 == false); const bool unique9 = unique_types::value; assert(unique9 == false); const bool unique10 = unique_types::value; assert(unique10 == false); const bool unique11 = unique_types::value; assert(unique11 == false); const bool unique12 = unique_types::value; assert(unique12 == false); const bool unique13 = unique_types::value; assert(unique13 == false); }