检查可变参数的唯一性参数
我想variadic模板参数必须唯一。 我知道当多inheritance时,不允许相同的类inheritance。
struct A{}; struct B: A, A{}; // error
使用这个规则,我做了一个小小的代码。
#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. }
但是我的程序没有像我期望的那样工作。 怎么了?
/ /更新:/ /谢谢,我解决我的错误:/ /
// #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. // } //
问:有人可以解释,为什么失败?
UPDATE2:我以前的更新是非法的:))。 法律forms,但它编制了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; }
传递一个指向base_all<U...>
的指针只需要声明base_all<U...>
。 如果不尝试访问定义,编译器将不会检测到该types实际上是不确定的。 减轻这个问题的一个方法是使用一个需要base_all<U...>
定义的参数,例如:
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; }
虽然上面的问题回答了这个问题,但是却无法编译:创build的多重inheritance不在SFINAE考虑的合适的上下文中。 我不认为你可以利用这个规则,不允许从两次inheritance相同的基地。 相关的testing可以以不同的方式实现,但是:
#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 , "!"); }
另一个O(logN)实例化深度解决scheme。 它仍然需要重大清理,注释,命名空间,重命名和减less代码重复。
再次声明Xeo ,它的O(logN)实例化深度版本gen_seq
this(再次)依赖于。
#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>{};
除了索引序列生成之外,这个解决scheme甚至应该具有O(1)实例化深度。 它不是使用多重inheritance,而是使用std::array<std::false_type, size>
通过SFINAE执行O(1) – 实例化深度或。
执行is_one_of
。 注意“是其中之一”是“独特”的相反概念。
#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; } };
执行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)>{})> {};
用法示例:
#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; }