C++ 检查可变模板参数的唯一性
我希望可变模板参数必须唯一。 我知道当多重继承时,相同的类继承是不允许的C++ 检查可变模板参数的唯一性,c++,c++11,variadic-templates,typetraits,C++,C++11,Variadic Templates,Typetraits,我希望可变模板参数必须唯一。 我知道当多重继承时,相同的类继承是不允许的 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 ..
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.
}
#包括
模板结构id{};
模板struct base\u all:id。。。{};
模板<类。。。T>
结构是唯一的
{
模板
静态constexpr bool测试(base_all*)noexcept{return true;}
模板
静态constexpr bool测试(…)noexcept{return false;}
静态constexpr布尔值=测试(0);
};
int main()
{
constexpr bool b=is_unique::value;//false——为什么?
constexpr bool c=is_unique::value;//false
静态断言(b==true&&c==false,“!”;//失败。
}
但我的计划并没有像我预期的那样有效。怎么了
//更新:
//谢谢,我修复了我的错误:
//
/#包括
//#包括
//
//模板<类。。。U> 结构包{};
//
//模板结构id{};
//模板struct base\u all;
//模板<类。。。T> 结构基础\u所有:id。。。{};
//
//
//
//模板<类。。。T>
//结构是唯一的
// {
//模板)>
//结构检查;
//
//模板
//静态constexpr bool测试(检查*)无异常{return true;}
//
//模板
//静态constexpr bool测试(…)noexcept{return false;}
//
//静态constexpr布尔值=测试(0);
// };
//
//int main()
// {
//constexpr bool b=is_unique::value;//true
//constexpr bool c=is_unique::value;//false
//
//静态断言(b==true&&c==false,“!”;//成功。
// }
//
问:有人可以解释,为什么失败了
更新2:我以前的更新是非法的:))。法律形式,但它的编译时间
#包括
#包括
#包括
名称空间mpl
{
模板使用invoke=typename T::type;
使用if_t=invoke>的模板;
模板结构id{};
结构空{};
模板结构基:A,B{};
模板结构是唯一的\u impl;
模板结构是唯一的\u impl:std::true\u type{};
模板
struct is_unique_impl:if_t,std::false_type,is_unique_impl {};
模板结构是唯一的:是唯一的\u impl{};
}//mpl
int main()
{
constexpr bool b=mpl::is_unique::value;
constexpr bool c=mpl::is_unique::value;
静态断言(b==true,“!”;
静态断言(c==false,“!”;
返回0;
}
传递指向base\u all
的指针只需要存在base\u all
的声明。如果不尝试访问定义,编译器将不会检测到类型实际上定义错误。缓解该问题的一种方法是使用需要定义base\u all
的参数,例如:
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;
}
templatestruct base\u all
:id。。。
{
typedef int类型;
};
// ...
模板<类。。。U>
静态constexpr bool测试(typename base_all::type)无异常
{
返回true;
}
尽管上面回答了这个问题,但它无法编译:创建的多重继承不适合SFINAE。我不认为你可以利用这个规则,不允许从两次继承相同的基础。相关测试可以以不同方式实施,但:
#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 , "!");
}
#包括
模板
结构是其中之一;
模板
结构是其中之一
{
静态constexpr bool值=false;
};
模板
结构是其中之一
{
静态constexpr bool value=std::is_same::value
||是::value的一个;
};
模板
结构是唯一的;
模板
结构是唯一的{
静态constexpr布尔值=真;
};
模板
结构是唯一的
{
静态constexpr bool value=is_unique::value
&&!是::value的其中之一;
};
int main()
{
constexpr bool b=is_unique::value;
constexpr bool c=is_unique:value;
静态断言(b==true&&c==false,“!”;
}
另一个O(logN)实例化深度解决方案。它仍然需要大量清理、注释、名称空间、重命名和减少代码重复
再次感谢,这(再次)依赖于谁
#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>{};
的实现是唯一的
:
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)>{})>
{};
名称空间详细信息
{
//带有泛型无约束的`Any`类型
//放弃函数模板的多个参数的步骤
模板
构造任何
{
模板
constexpr Any(T&){}
};
//“wrapper”用作“declval”的替代词,
//如果'T'是参考,则可以跟踪
模板
结构包装器{using type=T;};
模板
结构是包的一部分
{
模板
结构辅助程序
{
模板
静态constexpr bool推断剩余(任意…,剩余…)
{
//独特是其中之一
返回不是{}中的一个;
}
};
模板
静态constexpr bool推断_seq(seq)
{
return helper::模板推断剩余(wrapper()…);
}
静态constexpr bool create_seq()
{
返回推断顺序(gen_seq{});
}
使用type=std::integral_常量;
};
模板
constexpr自动SFINAE(内部)
->decltype(std::array)
{{typename Packs::type{}…}})
{返回{};/*仅用于抑制警告*/}
模板
静态constexpr int SFINAE(…){return 42;}
模板
constexpr bool测试()
{
返回
#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>{};
#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; }
};
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;
}
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);
}
#include <type_traits>
template <typename T>
struct Base{};
template <typename... Ts>
struct TypeSet : Base<Ts>...
{
template<typename T>
constexpr auto operator+(Base<T>)
{
if constexpr (std::is_base_of_v<Base<T>, TypeSet>)
return TypeSet{};
else
return TypeSet<Ts..., T>{};
}
constexpr auto size() const -> std::size_t
{
return sizeof...(Ts);
}
};
template<typename... Ts>
constexpr auto are_unique() -> bool
{
constexpr auto set = (TypeSet<>{} + ... + Base<Ts>{});
return set.size() == sizeof...(Ts);
}
int main()
{
static_assert(are_unique<int, float, char, char*>());
static_assert(not are_unique<int, float, char, char>());
}
template<typename T, typename... Types>
constexpr bool are_types_unique_v = (!std::is_same_v<T, Types> && ...) && are_types_unique_v<Types...>;
template<typename T>
constexpr bool are_types_unique_v<T> = true;