简体   繁体   中英

How to statically check if type T of a template is std::vector<U>, where U is either float, double or integral

How can I check that arguments in parameter pack have type of either of float , double , integral , or a std::vector of thereof?

For example T={int, long, std::vector<double>} is fine,

while T={int, long, std::vector<long double>} is not, because we don't allow std::vector to be of long double type.

I got this far

template<class ...T>
void foo(T... t)
{
    static_assert(std::is_same<float, T...>::value
               || std::is_same<double, T...>::value
               || std::is_integral<T...>::value
            /* || std::is_same<std::vector<float/double/integral>, T>::value ? */
               , "unsupported type!");
}

and not sure how to express the restriction of std::vector .

It would be nice to reuse the float/double/integral check somehow, so that we wouldn't need to type them twice. Something like

bool basic_check = std::is_same<float, T...>::value
               || std::is_same<double, T...>::value
               || std::is_integral<T...>::value;

static_assert(basic_check
              || std::is_same<std::vector<basic_check>, T>
              , "unsupported type!");

I also want the assert to succeed (ie pass the build) when T={} .

You can create your own check, using template specialization. I've broadened the check to include long double just to reduce the size of the code.

#include <type_traits>
#include <vector>

template<class T>
struct is_ok {
    static constexpr bool value =
        std::is_floating_point<T>::value ||
        std::is_integral<T>::value;
};

template<class T>
struct is_ok<std::vector<T>> {
    static constexpr bool value =
        std::is_floating_point<T>::value ||
        std::is_integral<T>::value;
};

Here is a demo:

#include <cstdio>
#define TEST(x) \
    std::printf("%s: %s\n", #x, is_ok<x>::value ? "true" : "false")

int main() {
    TEST(int);
    TEST(float);
    TEST(char *);
    TEST(std::vector<int>);
    TEST(std::vector<float>);
    TEST(std::vector<char *>);
    return 0;
}

Output:

int: true
float: true
char *: false
std::vector<int>: true
std::vector<float>: true
std::vector<char *>: false

First write a trait that tests one type:

template<class T>
struct is_ok : std::is_arithmetic<T> { };

template<class T, class A>
struct is_ok<std::vector<T, A>> : std::is_arithmetic<T> { };

Then test that the trait holds true for every type in the pack. I prefer to use @Columbo's bool_pack trick:

template<bool...> class bool_pack;
template<bool... b>
using all_true = std::is_same<bool_pack<true, b...>, bool_pack<b..., true>>;

template<class ...T>
void foo(T... t)
{
    static_assert( all_true<is_ok<T>::value...>::value, "unsupported type!");
}

You can use a helper class to do what you want to do.

template<class ...T> struct is_float_or_integral;

template <typename T> struct is_float_or_integral<T>
{
   static const bool value =
      std::is_integral<T>::value ||
      std::is_floating_point<T>::value;
};

// Use this if you want vector<vector<int>> to be OK.
template <typename T> struct is_float_or_integral<std::vector<T>>
{
   static const bool value = is_float_or_integral<T>::value;
};

// Use this if you don't want vector<vector<int>> to be OK.
template <typename T> struct is_float_or_integral<std::vector<T>>
{
   static const bool value =
      std::is_integral<T>::value ||
      std::is_floating_point<T>::value;
};

template <typename T1, typename ...T> struct is_float_or_integral<T1, T...>
{
   static const bool value =
      is_float_or_integral<T1>::value &&
      is_float_or_integral<T...>::value;
};

and use it as:

template<class ...T>
void foo(T... t)
{
   static_assert(is_float_or_integral<T...>::value == true, "Problem");
   //
   // ... Rest of your function.
}

Function overloads allow for somewhat simpler specification of restrictions on vector element types, and a trivial ignore helper function that takes arguments and discards them allows you to easily add the relevant check for every parameter:

#include <type_traits>
#include <vector>

template <typename T>
auto is_valid(T) ->
  std::integral_constant<
    bool,
    std::is_integral<T>::value
      || std::is_same<T, float>::value
      || std::is_same<T, double>::value>;

template <typename T>
auto is_valid(std::vector<T>) ->
  std::integral_constant<
    bool,
    std::is_integral<T>::value
      || std::is_same<T, float>::value
      || std::is_same<T, double>::value>;

void ignore(...) { }

template<class ...T>
void foo(T... t)
{
    ignore([]() {
      static_assert(decltype(is_valid(t))::value, "unsupported type!");
    }...);
}

@Dietrich Epp's and @TC's solutions were good, except that the first one didn't explain much how to use it in the variadic template case that I had in my question, and the latter didn't explain how to extend the solution to more than one constrain check (though @TC later put it in comments, but I already used their merged solution before he got to comment).

Anyway, here is their merged solution, if anyone is interested. It also reuses the base type restriction, as asked in the question.

// base type restrictions
template<class T>
struct is_ok_base
{
    static constexpr bool value =
        std::is_same<float, T>::value
        || std::is_same<double, T>::value
        || std::is_integral<T>::value;
};

// allow any types that is_ok_base accepts
template<class T>
struct is_ok
{
    static constexpr bool value = is_ok_base<T>::value;
};

// allow any types for std::vector that is_ok_base accepts
template<class T>
struct is_ok<std::vector<T>>
{
    // replace is_ok_base<T>::value with is_ok<T>::value if you want to allow
    // nested vectors
    static constexpr bool value = is_ok_base<T>::value;
};

// some boilerplate to make the checks above work with variadic templates
template<bool...> class bool_pack;
template<bool... b>
using all_true = std::is_same<bool_pack<true, b...>, bool_pack<b..., true>>;

// the actual check
template<class ...T>
constexpr void static_type_check()
{
    static_assert(all_true<is_ok<T>::value...>::value, "Unsupported type!");
}

template<class ...T>
void foo(T... t)
{
    static_type_check<T...>(); // or just copypaste the static_assert into here
}

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM