How to apply std::is_convertible_v to a set of variadic arguments?

Viewed 284

I would like to check that if a list of arguments is convertible to size_t or not. I think maybe the std::is_convertible_v is the right tool from STL. Here is what I imagine it should be (which is wrong):

#include <type_traits>

template<typename... Args>
constexpr bool check_all_convertible ()
{
    return std::is_convertible_v<Args,size_t>;
}

I finally want to use the check_all_convertible() to enable another function:

template<typename... Args>
std::enable_if_t<check_all_convertible<Args...>(), void> 
    some_function (Args... args) {}

Question

  • How can I use std::is_convertible_v for variadic arguments?
  • Do you suggest a better approach from STL?

I appreciate any suggestion to improve the code above.

3 Answers

You can use a fold expression:

template<typename... Args>
std::enable_if_t<(std::is_convertible_v<Args,size_t> && ...)> 
some_function (Args... args) {}

C++20 solution (Yes!! We finally have concepts!!!):

#include <concepts>

auto some_function(std::convertible_to<std::size_t> auto ... args)
{
}

See it live on godbolt.

The auto is needed because the committee reasoned without it the declaration would deceptively look like a function (but it is a template). Bjarne thinks they will regret it down the line. Anyway we should get used to typing auto all the time because from now on we shouldn't write unconstrained templates.

For completeness here is the longer version (but I recommend the terse one):

template <std::convertible_to<std::size_t>... Args>
auto some_function2(Args... args)
{
}

And the longer longer version:

template <class... Args>
    requires (std::convertible_to<Args, std::size_t> && ...)
auto some_function3(Args... args)
{
}

As I found out you have C++17 support, but in case of C++11/C++14 you can do following (maybe you will need it in the future):

template<
    typename    To,
    typename    From
    typename... Rest
> constexpr bool is_converible_variadic_impl()
{
    return \
        std::is_convertible<From, To>::value && 
        is_converible_variadic_impl<To, Rest...>();
}

template<
    typename To,
    typename From
> constexpr bool is_converible_variadic_impl()
{
    return std::is_convertible<From, To>::value;
}

/*************************************************/

template<
    typename    To,
    typename... From
> struct is_convertible_variadic
   : std::integral_constant<bool, is_converible_variadic_impl<To, From...>()>
{ };

template<
    typename    To,
    typename... From
> constexpr bool is_convertible_variadic_v = is_convertible_variadic<To, From...>::value;

For size_t use it as in following example:

template<typename... Args>
std::enable_if_t<std::is_convertible_variadic_v<size_t, Args...> > 
some_function (Args... args) {}

Maybe it is confusing to put "To" type in the beginning (mismatch with STL) of template parameters, but I think it is not such complicated task to fix it :)

Related