enable_if 中的短路运算符

Short Circuiting Operators in an enable_if

我想编写一个接受 array<int, 3>int[3] 的模板化函数。我试图在 enable_if:

中捕获它
template<typename T>
enable_if_t<is_array_v<T> && extent_v<T> == 3U || !is_array_v<T> && tuple_size<T>::value == 3U> foo(const T& param) {}

不幸的是,对于 int[3]tupple_size 未定义,这会导致模板在评估短路之前无法编译。

我也尝试过使用 conditional 来做到这一点,但在考虑条件之前确保两个选项都对 T 有效是同样的问题。

我知道我可以通过专业化来做到这一点。但是函数体中的代码是完全相同的。我讨厌这样的事实,即当实现相同时我会专攻。

有没有办法在评估条件之前强制短路?

我建议另一种方法:调用包含公共代码的公共函数的 2 个重载(总是更喜欢重载而不是模板特化):

namespace detail
{
template <class T>
auto foo_impl(const T& a)
{
    // common code
}
}

template <class T>
auto foo(const std::array<T, 3>& a)
{
    detail::foo_impl(a);
}

template <class T>
auto foo(const T(&a)[3])
{
    detail::foo_impl(a);
}

这很清楚,没有麻烦,并且避免了代码重复。

另一种方法是创建自己的特征:

template <class T, std::size_t Size>
struct my_is_array : std::false_type
{};

template <class T, std::size_t Size>
struct my_is_array<std::array<T, Size>, Size> : std::true_type
{};

template <class T, std::size_t Size>
struct my_is_array<T[Size], Size> : std::true_type
{};

template<typename T>
std::enable_if_t<my_is_array<T, 3>::value> foo(const T& param) {}

或者(其实我更喜欢这个):

template <class T>
struct array_size_or_zero : std::integral_constant<std::size_t, 0>
{};

template <class T, std::size_t Size>
struct array_size_or_zero<std::array<T, Size>> : std::integral_constant<std::size_t, Size>
{};

template <class T, std::size_t Size>
struct array_size_or_zero<T[Size]> : std::integral_constant<std::size_t, Size>
{};

template<typename T>
std::enable_if_t<array_size_or_zero<T>::value == 3> foo(const T& param) {}

小心!!:foo 必须有引用参数,否则数组会衰减为指针。

简而言之,模板替换必须始终有效。定义一个特定的模板来匹配数组可能会更容易:

template <typename T>
struct IsArrayInt3 { enum: bool { value = false }; };

template <>
struct IsArrayInt3<int[3]> { enum: bool { value = true }; };

template <>
struct IsArrayInt3<std::array<int, 3>> { enum: bool { value = true }; };

利用非数组类型的 extent<T> 为零因此是假的这一事实,并且 disjunction 从列表中的第一个真实类型导出并短路:

template<typename T>
enable_if_t<disjunction<extent<T>, tuple_size<T>>::value == 3U> foo(const T& param) {}

这可能太聪明了。请注意,您不能在此处使用 disjunction_v


conditional 应该也能正常工作。诀窍是在选择正确的类型之前不要要求 ::value

template<typename T>
enable_if_t<conditional_t<is_array_v<T>, extent<T>, tuple_size<T>>::value == 3U> 
    foo(const T& param) {}