C++ 如何使静态断言与SFINAE配合使用
更新 我发布了一份C++ 如何使静态断言与SFINAE配合使用,c++,c++14,template-meta-programming,sfinae,static-assert,C++,C++14,Template Meta Programming,Sfinae,Static Assert,更新 我发布了一份rebind的工作草稿,作为对这个问题的回答。尽管我没有找到一种通用的方法来防止static\u asserts破坏元函数,但我还是很幸运 基本上,我想检查模板类型T是否可以从其他类型T构建。其中T和Args…在这两种类型中是相同的。问题是,T中可能有一个static\u assert,它完全破坏了我的元函数 下面是我试图做的一个粗略的总结 template<typename T> struct fake_alloc { using value_type
rebind
的工作草稿,作为对这个问题的回答。尽管我没有找到一种通用的方法来防止static\u assert
s破坏元函数,但我还是很幸运
基本上,我想检查模板类型
T
是否可以从其他类型T
构建。其中T
和Args…
在这两种类型中是相同的。问题是,T
中可能有一个static\u assert
,它完全破坏了我的元函数
下面是我试图做的一个粗略的总结
template<typename T>
struct fake_alloc {
using value_type = T;
};
template<typename T, typename Alloc = fake_alloc<T>>
struct fake_cont {
using value_type = T;
// comment the line below out, and it compiles, how can I get it to compile without commenting this out???
static_assert(std::is_same<value_type, typename Alloc::value_type>::value, "must be the same type");
};
template<typename T, typename U, typename = void>
struct sample_rebind {
using type = T;
};
template<template<typename...> class Container, typename T, typename U, typename... OtherArgs>
struct sample_rebind<
Container<T, OtherArgs...>,
U,
std::enable_if_t<
std::is_constructible<
Container<T, OtherArgs...>,
Container<U, OtherArgs...>
>::value
>
>
{
using type = Container<U, OtherArgs...>;
};
static_assert(
std::is_same<
fake_cont<int, fake_alloc<int>>,
typename sample_rebind<fake_cont<int>, double>::type
>::value,
"This should pass!"
);
模板
结构假匹配{
使用值_type=T;
};
模板
结构假(续){
使用值_type=T;
//把下面的行注释掉,它就编译了,我怎样才能让它编译而不注释掉呢???
静态断言(std::is_same::value,“必须是同一类型”);
};
模板
结构示例重新绑定{
使用类型=T;
};
模板
结构示例重新绑定<
集装箱,
U
std::如果启用,则启用<
std::是可构造的吗<
集装箱,
容器
>::价值
>
>
{
使用类型=容器;
};
静态断言(
std::是一样的吗<
伪造的(续),
typename示例绑定::type
>::价值,
“这应该过去!”
);
正如您所看到的,期望的行为是最终的static\u assert
应该通过,但不幸的是,当fake\u cont
尝试调用fake\u cont
的构造函数时,会触发static\u assert
中的static\u assert
在真正的代码中,false\u cont
是libc++的std::vector
,所以我不能修改它的内脏,或者std::is\u constructible
任何关于解决这一特定问题的建议都将受到赞赏,特别是关于解决静态断言的一般建议
编辑:第一部分应该是相同的false\u cont
编辑2:如果在false\u cont
中注释掉static\u assert
,它将编译(clang 3.5)。这就是我想要的。所以我只需要一些方法来避免false\u cont
中的static\u assert
namespace details {
template<class T,class=void>
struct extra_test_t: std::true_type {};
}
或者我们可以声明,任何具有分配器类型的都可能无法恢复
解决此问题的一种更具容器意识的方法是提取分配器类型(::allocator_type
),并用T
到U
的重新绑定替换容器参数列表中分配器类型的所有实例。这仍然是一个棘手的问题,因为std::map
有一个类型为std::allocator
的分配器,并且不能以通用的方式区分键int
和值int
。我已经成功地获得了重新绑定的非常可靠的初稿。它适用于所有STL容器(不包括不太常见的模板参数组合)、容器适配器和std::integer\u序列
。而且它可能也适用于更多的事情。但它肯定不会适用于所有情况
主要的问题是让地图类型像雅克预测的那样工作,但是一点类型特征可以帮助解决这个问题
接下来是代码
void\u t
template<class...>
using void_t = void;
template<class... Types>
struct pack
{
template<class T, class U>
using replace = pack<
std::conditional_t<
std::is_same<Types, T>{},
U,
Types
>...
>;
template<class T, class U>
using replace_or_rebind = pack<
std::conditional_t<
std::is_same<Types, T>{},
U,
typename rebind<Types, U>::type
>...
>;
template<class Not, class T, class U>
using replace_or_rebind_if_not = pack<
std::conditional_t<
std::is_same<Types, Not>{},
Types,
std::conditional_t<
std::is_same<Types, T>{},
U,
typename rebind<Types, U>::type
>
>...
>;
template<class T>
using push_front = pack<T, Types...>;
};
这将处理一些简单的类似列表的类型操作
replace
以非递归方式将所有出现的T
替换为U
replace_或_rebind
将所有出现的T
替换为U
,对于所有不匹配的出现,调用rebind
replace\u或rebind\u如果不
与replace\u或rebind
相同,但跳过任何匹配的元素not
push_front
只需将一个元素推到类型列表的前面
呼叫成员重新绑定
// has member rebind implemented as alias
template<class T, class U, class = void>
struct do_mem_rebind
{
using type = typename T::template rebind<U>;
};
// has member rebind implemented as rebind::other
template<class T, class U>
struct do_mem_rebind<T, U, void_t<typename T::template rebind<U>::other>>
{
using type = typename T::template rebind<U>::other;
};
这将获取一个包
,提取其中包含的类型,并将它们放入另一个模板C
重新绑定实施
好东西
template<class T, class U, bool = is_map_like<T>{}, bool = std::is_lvalue_reference<T>{}, bool = std::is_rvalue_reference<T>{}, bool = has_mem_rebind<T, U>{}>
struct rebind_impl
{
static_assert(!is_template_instantiation<T>{}, "Sorry. Rebind is not completely implemented.");
using type = T;
};
// map-like container
template<class U, template<class...> class C, class First, class Second, class... Others>
class rebind_impl<C<First, Second, Others...>, U, true, false, false, false>
{
using container_type = C<First, Second, Others...>;
using value_type = typename container_type::value_type;
using old_alloc_type = typename container_type::allocator_type;
using other_replaced = typename pack<Others...>::template replace_or_rebind_if_not<old_alloc_type, First, typename U::first_type>;
using new_alloc_type = typename std::allocator_traits<old_alloc_type>::template rebind_alloc<std::pair<std::add_const_t<typename U::first_type>, typename U::second_type>>;
using replaced = typename other_replaced::template replace<old_alloc_type, new_alloc_type>;
using tail = typename replaced::template push_front<typename U::second_type>;
public:
using type = unpack_t<C, typename tail::template push_front<typename U::first_type>>;
};
// has member rebind
template<class T, class U>
struct rebind_impl<T, U, false, false, false, true>
{
using type = typename do_mem_rebind<T, U>::type;
};
// has nothing, try rebind anyway
template<template<class...> class C, class T, class U, class... Others>
class rebind_impl<C<T, Others...>, U, false, false, false, false>
{
using tail = typename pack<Others...>::template replace_or_rebind<T, U>;
public:
using type = unpack_t<C, typename tail::template push_front<U>>;
};
// has nothing, try rebind anyway, including casting NonType template parameters
template<class T, template<class, T...> class C, class U, T FirstNonType, T... Others>
struct rebind_impl<C<T, FirstNonType, Others...>, U, false, false, false, false>
{
using type = C<U, U(FirstNonType), U(Others)...>;
};
// array takes a non-type parameter parameters
template<class T, class U, std::size_t Size>
struct rebind_impl<std::array<T, Size>, U, false, false, false, false>
{
using type = std::array<U, Size>;
};
// pointer
template<class T, class U>
struct rebind_impl<T*, U, false, false, false, false>
{
using type = typename std::pointer_traits<T*>::template rebind<U>;
};
// c-array
template<class T, std::size_t Size, class U>
struct rebind_impl<T[Size], U, false, false, false, false>
{
using type = U[Size];
};
// c-array2
template<class T, class U>
struct rebind_impl<T[], U, false, false, false, false>
{
using type = U[];
};
// lvalue ref
template<class T, class U>
struct rebind_impl<T, U, false, true, false, false>
{
using type = std::add_lvalue_reference_t<std::remove_reference_t<U>>;
};
// rvalue ref
template<class T, class U>
struct rebind_impl<T, U, false, false, true, false>
{
using type = std::add_rvalue_reference_t<std::remove_reference_t<U>>;
};
返回SFINAE和静态断言
在谷歌搜索了很多之后,似乎没有一种通用的方法可以像libc++的STL容器中的方法那样定义static\u assert
s。这真的让我希望这门语言有更友好的语言,但比概念更特别一点
比如:
模板
静态断言(缓存线大小==64,“”)
struct my_struct{…};
您的问题不清楚,因为您给出的示例根本不起作用。你是说重新绑定样本吗?即使这样,如果你保留其他参数…
,也会导致伪造。目前,编译器拒绝您的代码是正确的。我在fake\u cont
中注释掉了static\u assert
,它确实进行了编译(至少在clang 3.5上),传递了最终的static\u assert。我不是说编译器是错的,我只是问我如何才能使编译器在专业化的可构造的部分不失败。请注意,如果类型不可构造,则返回它传递的原始类型,因为您使用了using type=T代码>在非专门化的情况下(然后使用)。你只是把三个错误加在一起才通过了!我知道,这正是我想要的。忽略示例重新绑定的预期行为是。。。它只是为了演示我想通过的测试。。。或者真的失败了。我只是想避免中的静态断言
,中关于替换的注释::分配器类型
是个好主意,您关于map的警告似乎可以实现
template<class T, class = void>
struct is_map_like : std::false_type {};
template<template<class...> class C, class First, class Second, class... Others>
struct is_map_like<C<First, Second, Others...>,
std::enable_if_t<std::is_same<typename C<First, Second, Others...>::value_type::first_type,
std::add_const_t<First>>{} &&
std::is_same<typename C<First, Second, Others...>::value_type::second_type,
Second>{}>>
: std::true_type {};
template<class T, class U, class = void>
struct has_mem_rebind : std::false_type {};
template<class T, class U>
struct has_mem_rebind<T, U, void_t<typename T::template rebind<U>>> : std::true_type {};
template<class T>
struct is_template_instantiation : std::false_type {};
template<template<class...> class C, class... Others>
struct is_template_instantiation<C<Others...>> : std::true_type {};
template<class... Types>
struct pack
{
template<class T, class U>
using replace = pack<
std::conditional_t<
std::is_same<Types, T>{},
U,
Types
>...
>;
template<class T, class U>
using replace_or_rebind = pack<
std::conditional_t<
std::is_same<Types, T>{},
U,
typename rebind<Types, U>::type
>...
>;
template<class Not, class T, class U>
using replace_or_rebind_if_not = pack<
std::conditional_t<
std::is_same<Types, Not>{},
Types,
std::conditional_t<
std::is_same<Types, T>{},
U,
typename rebind<Types, U>::type
>
>...
>;
template<class T>
using push_front = pack<T, Types...>;
};
// has member rebind implemented as alias
template<class T, class U, class = void>
struct do_mem_rebind
{
using type = typename T::template rebind<U>;
};
// has member rebind implemented as rebind::other
template<class T, class U>
struct do_mem_rebind<T, U, void_t<typename T::template rebind<U>::other>>
{
using type = typename T::template rebind<U>::other;
};
template<template<class...> class C, class Pack>
struct unpack;
template<template<class...> class C, class... Args>
struct unpack<C, pack<Args...>> { using type = C<Args...>; };
template<template<class...> class C, class Pack>
using unpack_t = typename unpack<C, Pack>::type;
template<class T, class U, bool = is_map_like<T>{}, bool = std::is_lvalue_reference<T>{}, bool = std::is_rvalue_reference<T>{}, bool = has_mem_rebind<T, U>{}>
struct rebind_impl
{
static_assert(!is_template_instantiation<T>{}, "Sorry. Rebind is not completely implemented.");
using type = T;
};
// map-like container
template<class U, template<class...> class C, class First, class Second, class... Others>
class rebind_impl<C<First, Second, Others...>, U, true, false, false, false>
{
using container_type = C<First, Second, Others...>;
using value_type = typename container_type::value_type;
using old_alloc_type = typename container_type::allocator_type;
using other_replaced = typename pack<Others...>::template replace_or_rebind_if_not<old_alloc_type, First, typename U::first_type>;
using new_alloc_type = typename std::allocator_traits<old_alloc_type>::template rebind_alloc<std::pair<std::add_const_t<typename U::first_type>, typename U::second_type>>;
using replaced = typename other_replaced::template replace<old_alloc_type, new_alloc_type>;
using tail = typename replaced::template push_front<typename U::second_type>;
public:
using type = unpack_t<C, typename tail::template push_front<typename U::first_type>>;
};
// has member rebind
template<class T, class U>
struct rebind_impl<T, U, false, false, false, true>
{
using type = typename do_mem_rebind<T, U>::type;
};
// has nothing, try rebind anyway
template<template<class...> class C, class T, class U, class... Others>
class rebind_impl<C<T, Others...>, U, false, false, false, false>
{
using tail = typename pack<Others...>::template replace_or_rebind<T, U>;
public:
using type = unpack_t<C, typename tail::template push_front<U>>;
};
// has nothing, try rebind anyway, including casting NonType template parameters
template<class T, template<class, T...> class C, class U, T FirstNonType, T... Others>
struct rebind_impl<C<T, FirstNonType, Others...>, U, false, false, false, false>
{
using type = C<U, U(FirstNonType), U(Others)...>;
};
// array takes a non-type parameter parameters
template<class T, class U, std::size_t Size>
struct rebind_impl<std::array<T, Size>, U, false, false, false, false>
{
using type = std::array<U, Size>;
};
// pointer
template<class T, class U>
struct rebind_impl<T*, U, false, false, false, false>
{
using type = typename std::pointer_traits<T*>::template rebind<U>;
};
// c-array
template<class T, std::size_t Size, class U>
struct rebind_impl<T[Size], U, false, false, false, false>
{
using type = U[Size];
};
// c-array2
template<class T, class U>
struct rebind_impl<T[], U, false, false, false, false>
{
using type = U[];
};
// lvalue ref
template<class T, class U>
struct rebind_impl<T, U, false, true, false, false>
{
using type = std::add_lvalue_reference_t<std::remove_reference_t<U>>;
};
// rvalue ref
template<class T, class U>
struct rebind_impl<T, U, false, false, true, false>
{
using type = std::add_rvalue_reference_t<std::remove_reference_t<U>>;
};
template<class T, class U>
struct rebind : details::rebind_impl<T, U> {};
template<class T, class U>
using rebind_t = typename rebind<T, U>::type;
template<class T>
static_assert(CACHE_LINE_SIZE == 64, "")
struct my_struct { ... };