Получение количества аргументов функции во время компиляции с использованием шаблонов с переменным числом аргументов с проверкой типа аргумента в c ++ - PullRequest
0 голосов
/ 30 апреля 2018

Скажите, у меня есть класс:

template<typename... Types>
class Example
{
public:
    using types = std::tuple<Types...>;
    template<size_t N> using dim_type = std::tuple_element_t<N, types>;
};

И я хочу реализовать функцию-член, которая зависит от элемента кортежа, следующим образом с целью получения доступа к числу аргументов во время компиляции:

template<size_t N>
inline constexpr void do_stuff(const dim_type<N>& elems...)
{
    constexpr size_t size_stuff = sizeof...(elems); // <-- this is the end goal 
};

Проблема в том, что эта реализация do_stuff() не подойдет. И, в конце концов, я бы хотел, чтобы функция работала так:

Example<long,std::string> ex;
ex.do_stuff<0>(3);
ex.do_stuff<0>(5,6);
ex.do_stuff<1>("a","b","c","d");
ex.do_stuff<0>("a","b","c","d"); // <-- this line should not compile
ex.do_stuff<1>(1,"b"); // <-- nor should this one

А внутри do_stuff Я должен знать во время компиляции, сколько передано аргументов.

Один из возможных ответов, но у меня возникла проблема:

Я подумал, что правильная реализация этого потребует использования вариадических шаблонов, однако у меня проблема с получением части std::enable_if для работы:

template<size_t N, typename... Args,
    std::enable_if_t<static_and<std::is_convertible_v<Args, dim_type<N>>...>::value>* = nullptr>
inline constexpr void do_stuff(const Args&... elems)
{
    constexpr size_t size_stuff = sizeof...(elems); // <-- this is the end goal 
};

, где static_and:

template<bool Head, bool... Tail>
struct static_and {
    static constexpr bool value = Head && static_and<Tail...>::value;
};

template<bool Bool> struct static_and<Bool> {
    static constexpr bool value = Bool;
};

1 Ответ

0 голосов
/ 30 апреля 2018

Мне кажется, что правильный путь (возможный правильный) - это путь, основанный на static_and: список переменных типа Args аргумента, проверенного SFINAE на возможность преобразования в правильный тип.

Я предлагаю следующую версию static_and

template <bool ...>
struct static_and : public std::false_type
 { };

template <>
struct static_and<> : public std::true_type
 { };

template <bool ... Bs>
struct static_and<true, Bs...> : public static_and<Bs...>
 { };

и do_stuff() становятся

   template <std::size_t I, typename ... Ts>
   inline constexpr auto do_stuff (Ts const & ... elems)
      -> std::enable_if_t<
            static_and<std::is_convertible<Ts, type_n<I>>::value...>::value>
    {
      // now the number of elems is sizeof...(elems) 
      // or sizeof...(Ts)
      std::cout << sizeof...(Ts) << std::endl;
    }

Ниже приведен полный пример компиляции (с ошибками компиляции, когда это уместно)

#include <tuple>
#include <iostream>
#include <type_traits>    

template <bool ...>
struct static_and : public std::false_type
 { };

template <>
struct static_and<> : public std::true_type
 { };

template <bool ... Bs>
struct static_and<true, Bs...> : public static_and<Bs...>
 { };

template <typename ... Types>
struct foo
 {
   using types = std::tuple<Types...>;

   static constexpr std::size_t num_types { sizeof...(Types) };

   template <std::size_t I>
   using type_n = std::tuple_element_t<I, types>;

   template <std::size_t I, typename ... Ts>
   inline constexpr auto do_stuff (Ts const & ... elems)
      -> std::enable_if_t<
            static_and<std::is_convertible<Ts, type_n<I>>::value...>::value>
    {
      // now the number of elems is sizeof...(elems) 
      // or sizeof...(Ts)
      std::cout << sizeof...(Ts) << std::endl;
    }
 };

int main ()
 {
   foo<long, std::string> ex;

   ex.do_stuff<0>(3);                     // compile; print 1
   ex.do_stuff<0>(5, 6);                  // compile; print 2
   ex.do_stuff<1>("a", "b", "c", "d");    // compile; print 4
   // ex.do_stuff<0>("a", "b", "c", "d"); //  compilation error
   // ex.do_stuff<1>(1, "b");             //  compilation error
 }

Если вы можете использовать C ++ 17, вместо static_and вы можете просто использовать свертывание шаблонов

template <std::size_t I, typename ... Ts>
inline constexpr auto do_stuff (Ts const & ... elems)
   -> std::enable_if_t<(... && std::is_convertible<Ts, type_n<I>>::value)>
 {
   // now the number of elems is sizeof...(elems) 
   // or sizeof...(Ts)
   std::cout << sizeof...(Ts) << std::endl;
 }

Если в C ++ 14 вы предпочитаете функцию constexpr static_and() вместо struct, вы можете написать ее следующим образом

template <bool ... Bs>
constexpr bool static_and ()
 {
   using unused = bool[];

   bool ret { true };

   (void) unused { true, ret &= Bs... };

   return ret;
 }

Так do_stuff() становится

   template <std::size_t I, typename ... Ts>
   inline constexpr auto do_stuff (Ts const & ... elems)
      -> std::enable_if_t<
            static_and<std::is_convertible<Ts, type_n<I>>::value...>()>
    {
      // now the number of elems is sizeof...(elems) 
      // or sizeof...(Ts)
      std::cout << sizeof...(Ts) << std::endl;
    }
...