在编译时使用可变参数模板在 c++ 中使用参数类型检查获取函数的参数数

Getting the number of arguments to a function at compile-time using variadic templates with argument type check in c++

本文关键字:参数 检查 类型 函数 数数 获取 编译 变参 c++      更新时间:2023-10-16

假设我有一个类:

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

在我看来,正确的方法(一种可能的正确方法)是基于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 中,您更喜欢constexprstatic_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;
}