2013-09-24 28 views
21

Tôi muốn các tham số mẫu variadic phải là duy nhất. Tôi biết khi nào đa thừa kế, thừa kế lớp giống hệt nhau không được phép.kiểm tra các tham số mẫu variadic cho tính duy nhất

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

Sử dụng quy tắc này, tôi đã tạo một mã nhỏ.

#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. 
} 

Nhưng chương trình của tôi không hoạt động như tôi mong đợi. Chuyện gì vậy?

// UPDATE: // Cảm ơn, tôi sửa chữa lỗi của tôi: //

//  #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. 
//  } 
// 

Q: ai đó có thể giải thích, lý do tại sao nó thất bại?

CẬP NHẬT 2: Bản cập nhật trước của tôi là bất hợp pháp :)). Hình thức pháp lý, nhưng nó đã biên soạn thời gian O (N).

#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; 

} 
+3

+1 cho ý tưởng – Asaf

Trả lời

10

Đi qua một con trỏ đến base_all<U...> chỉ đòi hỏi sự tồn tại của một bản tuyên bố của base_all<U...>. Không cố gắng truy cập định nghĩa, trình biên dịch sẽ không phát hiện ra rằng kiểu dữ liệu này thực sự không được xác định. Một cách tiếp cận để giảm thiểu vấn đề đó sẽ được sử dụng một cuộc tranh cãi mà đòi hỏi một định nghĩa của base_all<U...>, ví dụ:

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; 
} 

Mặc dù câu trả lời trên các câu hỏi, nó không biên dịch: sự kế thừa nhiều tạo ra không phải là trong một phù hợp bối cảnh để được xem xét cho SFINAE. Tôi không nghĩ rằng bạn có thể tận dụng quy tắc trên không cho phép cùng một cơ sở được kế thừa từ hai lần. Tuy nhiên, thử nghiệm có liên quan có thể được thực hiện khác nhau:

#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 , "!"); 
} 
+0

Vì vậy, những gì bạn đang nói là cả hai 'b' và 'c' phải là 'true', đúng không? Tuy nhiên, chúng đều là 'sai'. Tôi nghĩ câu hỏi thực sự là điều gì gây ra SFINAE nếu không có nhiều thừa kế? – jrok

+0

@jrok: phần đó dường như không thể chuyển đổi '0' thành con trỏ.Bằng cách nào đó '0' không muốn được chuyển đổi thành' base_all * 'mặc dù tôi đã không tìm ra lý do tại sao. –

+0

@Dietmar Kuhl. Cảm ơn bạn đã trả lời, nhưng Tôi biết giải pháp đệ quy, nhưng nó cần những cuộc tiếp thu sâu và chuyên môn. Hãy thử kiểm tra với 500 yếu tố, bạn sẽ cảm thấy khác biệt)). –

3

Một giải pháp chiều sâu tức thời khác O (logN). Nó vẫn cần một dọn dẹp, bình luận, không gian tên, đổi tên và giảm trùng lặp mã.

Kudos một lần nữa để Xeo, có O(logN) instantiation depth version of gen_seq điều này (một lần nữa) dựa vào.

#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>{}; 

Ngoài việc tạo chuỗi chỉ mục, giải pháp này thậm chí phải có O (1) độ sâu instantiation. Thay vì thừa kế nhiều, nó sử dụng một số std::array<std::false_type, size> để thực hiện O (1) -giải sâu OR qua SFINAE.

Triển khai is_one_of. Lưu ý rằng "là một trong" là khái niệm ngược lại của "là duy nhất".

#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; } 
}; 

Thực hiện 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)>{})> 
{}; 

Cách sử dụng Ví dụ:

#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; 
} 
+0

N.B. Tôi ngạc nhiên rằng thủ thuật 'std :: array' được chấp nhận bởi g ++ 4.8.1 và clang ++ 3.4. Nó không giống như một bối cảnh ngay lập tức với tôi. – dyp