如何使static_assert与SFINAE一起玩得很好

xcv*_*cvr 6 c++ static-assert sfinae template-meta-programming c++14

更新

我发布了一份工作草案,rebind作为问题的答案.虽然我没有太多运气找到一种通用的方法来防止static_assert破坏元功能.


基本上我想检查是否T<U, Args...>可以从其他类型构造模板化类型T<V, Args...>.其中TArgs...是在这两种类型相同.问题是,T<>可能有一个static_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!"
);
Run Code Online (Sandbox Code Playgroud)

正如您所看到的那样,期望的行为是最终static_assert应该通过,但不幸的是,它甚至没有达到这一点,因为static_assert在尝试调用构造函数fake_cont时触发了in .std::is_constructible<>fake_cont

在真正的代码fake_cont是libc ++的std::vector,所以我不能修改它的胆量或std::is_constructible胆量.

对于解决这个特定问题的任何建议都表示赞赏,static_assert特别感谢SFINAE周围的任何建议.

编辑:is_same的第一部分应该是 fake_cont<int, fake_alloc<int>>

编辑2:如果您注释掉static_assertfake_cont,它编译(铛3.5).这就是我想要的.所以我只需要一些方法来避免static_assert进入fake_cont.

Yak*_*ont 1

namespace details {
  template<class T,class=void>
  struct extra_test_t: std::true_type {};
}
Run Code Online (Sandbox Code Playgroud)

然后我们将额外的测试折叠到:

template<class...>struct types{using type=types;};

template<template<typename...> class Container, typename T, typename U, typename... OtherArgs>
struct sample_rebind<
  Container<T, OtherArgs...>,
  U,
  std::enable_if_t<
    details::extra_test_t< types< Container<T, OtherArgs...>, U > >::value
    && std::is_constructible<
      Container<T, OtherArgs...>,
      Container<U, OtherArgs...>
    >::value
  >
> {
  using type = Container<U, OtherArgs...>;
};
Run Code Online (Sandbox Code Playgroud)

我们编写额外的测试:

namespace details {
  template<class T, class Alloc, class U>
  struct extra_test_t<
    types<std::vector<T,Alloc>, U>,
    typename std::enable_if<
      std::is_same<value_type, typename Alloc::value_type>::value
    >::type
  > : std::true_type {};
  template<class T, class Alloc, class U>
  struct extra_test_t<
    types<std::vector<T,Alloc>, U>,
    typename std::enable_if<
      !std::is_same<value_type, typename Alloc::value_type>::value
    >::type
  > : std::false_type {};
}
Run Code Online (Sandbox Code Playgroud)

基本上,这让我们可以在测试中注入“补丁”以匹配static_assert.

如果我们有is_std_container<T>get_allocator<T>,我们可以写:

namespace details {
  template<template<class...>class Z,class T, class...Other, class U>
  struct extra_test_t<
    types<Z<T,Other...>>, U>,
    typename std::enable_if<
       is_std_container<Z<T,Other...>>>::value
       && std::is_same<
         value_type,
         typename get_allocator<Z<T,Other...>>::value_type
       >::value
    >::type
  > : std::true_type {};
  template<class T, class Alloc, class U>
  struct extra_test_t<
    types<std::vector<T,Alloc>, U>,
    typename std::enable_if<
       is_std_container<Z<T,Other...>>>::value
       && !std::is_same<
         value_type,
         typename get_allocator<Z<T,Other...>>::value_type
       >::value
    >::type
  > : std::false_type {};
}
Run Code Online (Sandbox Code Playgroud)

或者我们可以直接说任何有allocator_type可能的东西都不能反弹。

解决此问题的一种更具容器意识的方法是提取分配器类型 ( ),并以某种方式::allocator_type重新绑定 to 来替换容器参数列表中分配器类型的所有实例。这仍然很棘手,因为有一个类型的分配器,并且无法以通用方式区分键和值。TUstd::map<int, int>std::allocator< std::pair<const int, int> >intint