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) {}
我想编写一个接受 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) {}