Short Circuiting Operators in an enable_if

雨燕双飞 提交于 2019-12-22 07:59:24

问题


I want to write a templatized function which takes either an array<int, 3> or an int[3]. I'm trying to capture that in an 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) {}

Unfortunately for an int[3], tupple_size is not defined, which causes the template to fail to compile, before short circuiting is evaluated.

I have also tried to do this using a conditional but that has the same problem of ensuring both options are valid for T before considering the condition.

I know that I can do this by specializing. But the code is the exact same in the body of the function. I hate the fact that I'm specializing when the implementation is the same.

Is there a way I can force the short circuit before evaluating the conditions?


回答1:


Taking advantage of the fact that extent<T> for non-array types is zero and hence falsy, and disjunction derives from the first truthy type in the list with short circuiting:

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

This is probably too clever. Note that you can't use disjunction_v here.


conditional should work just fine too. The trick is to not ask for ::value until you've picked the right type:

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



回答2:


In short no, the template substitutions always have to be valid. It would probably be easier to just define a specific template to match the arrays:

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



回答3:


I would suggest a alternate approach: 2 overloads (always prefer overloads to template specializations) that call a common function which contains the common code:

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);
}

This is clear, hassle-free and avoids code repetition.

An alternate is to create your own trait:

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) {}

or (I actually like this one better):

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) {}

Careful!!: foo must have parameter by reference, otherwise the array decays to pointer.



来源:https://stackoverflow.com/questions/52079896/short-circuiting-operators-in-an-enable-if

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!