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