接受3种类型的无限相同数量的函数

时间:2014-01-17 20:00:51

标签: c++ function templates c++11 template-meta-programming

如何创建一个接受相同数量的字符串的函数 - int - char

func("aa","sd","zzz",123,13,3,'w','a','x') //correct
func("aa","sd",123,3,'w','x') //correct
func("aa",2,'w') //correct 
func()//correct 
func("aa","VV",23,'w') //error because 2 string 1 int and 1 char
func('a',2) //error 0 string 1 char 1 int  
.....

我想在编译时检查变量而不是在运行时检查变量,因此我无法使用std::vector

任何想法?有可能吗?

5 个答案:

答案 0 :(得分:4)

假设您的参数如下所示:

struct Param
{
  string mS;
  char mC;
  int mN;
};

不要试图组合一个带有未知或可变数量参数的函数,而是让函数取vector

void func (const vector <Param>& parameters)

更好的是,让函数将一对迭代器带到所述向量:

void func (vector <Param>::const_iterator begin, vector <Param>::const_iterator end)

更好的是,对容器和迭代器进行泛化:

template <typename Iterator>  void func (Iterator begin, Iterator end)

在最后一种情况下,迭代器可以引用符合迭代器限制的任何东西。这包括C风格的数组。您可以这样使用:

void Doer()
{
  Params params[] = 
  {
    Param("aa",123, 'W'),
    Param("sd",13,'a'),
    Param("zzz",3,'x')
  };
  const size_t numParams = sizeof (params) / sizeof (params[0]);
  func (params, params + numParams);
}

当然你需要一个构造函数:

struct Param
{ 
  Param (const std::string s, int n, char c)
  :
    mS (s),
    mN (n),
    mC (c)
  {
  }
// ...
};

在某些情况下,您可能需要制造商类型功能:

Param make_param (const std::string& s, int n, char c)
{
  Param retval (s, n, c);
  return retval;
}

答案 1 :(得分:4)

(这个问题最初被标记为C和C ++; C标签后来被删除了。以下部分内容特定于C,但是我将它留在这里,以防它对其他人有用。)

在C中,不,我不相信用编译时检查可以做到这一点。变量函数(a)至少需要一个固定参数(消除func()示例),以及(b)不在编译时检查可变参数的类型。 Variadic宏(C99中的新增功能)可能更有前途,但它们不执行类型检查。我已经考虑过如何使用嵌套通用选择(C11中的一个新功能,C ++中不存在),但我不相信它们可以解决问题。

如果你正在使用C(这个问题最初被标记为C和C ++),我认为你能得到的最接近的是为每个参数提供一个独特的函数:

void func0(void);
void func1(char*, int, char);    
void func2(char*, char*, int, int, char, char);
void func3(char*, char*, char*, int, int, int, char, char, char);

你知道你在写电话时你传递了多少参数,所以指定一个不同的功能并不是太大的负担。

在C ++中,您可以提供多个重载函数,每个函数都接受相应类型的参数:

void func();
void func(char*, int, char);    
void func(char*, char*, int, int, char, char);
void func(char*, char*, char*, int, int, int, char, char, char);

依此类推,因为你有耐心等多少过载。

这些解决方案都无限期地可扩展到任意数量的参数。

我原本以为C ++中没有通用解决方案,但Jarod42's answer(我还没有花时间去理解)似乎已经显示出来了。另一方面,为简单起见,有一些事情要说。另一方面,你只需要写一次。

还有一件事:你的最后一个例子是:

func('a',2) //error 0 string 1 char 1 int 

在C中,'a'2属于同一类型,即int。在C ++中,a的类型为char。 (同样,如果你只关心C ++,这不一定是相关的。)

答案 2 :(得分:2)

C ++ 11中的一个简单方法是:

template <std::size_t N>
void func(const std::array<const char*, N>& cstrings,
          const std::array<int, N>& ints
          const std::array<char, N>& chars);

所以你可以使用它:

func({ "aa","sd","zzz"}, {123, 13, 3} , {'w', 'a', 'x'});

如果你想使用相同的语法,它更详细并使用SFINAE: 它回退到用std::array调用版本,因为它更方便实现

#include <cstdint>
#include <array>
#include <tuple>
#include <type_traits>

#if 1 // Not in C++11

template <std::size_t ...> struct index_sequence {};

template <std::size_t I, std::size_t ...Is>
struct make_index_sequence : make_index_sequence < I - 1, I - 1, Is... > {};

template <std::size_t ... Is>
struct make_index_sequence<0, Is...> : index_sequence<Is...> {
    typedef index_sequence<Is...> type;
};

#endif

template <std::size_t N>
void func(const std::array<const char*, N>& cstrings,
          const std::array<int, N>& ints
          const std::array<char, N>& chars)
{
    // Add your code here
}

namespace detail
{
    template <typename T, typename SEQ, typename SEQ2> struct helper_split3;

    template <typename ... Ts, std::size_t ... Is, std::size_t ... Is2>
    struct helper_split3<std::tuple<Ts...>, index_sequence<Is...>, index_sequence<Is2...> >
    {
        typedef std::tuple<typename std::tuple_element<Is, std::tuple<Ts...> >::type...> First;
        typedef std::tuple<typename std::tuple_element<sizeof...(Is) + Is, std::tuple<Ts...> >::type...> Middle;
        typedef std::tuple<typename std::tuple_element<2 * sizeof...(Is) + Is2, std::tuple<Ts...> >::type...> Last;
    };

    template <typename T, typename ... Ts> struct are_same : std::true_type {};

    template <typename T, typename Tail, typename ... Ts>
    struct are_same<T, Tail, Ts...> :
        std::conditional<std::is_same<T, Tail>::value && are_same<T, Ts...>::value,
                        std::true_type, std::false_type>::type {};

    template <typename T, typename Tuple> struct are_same_in_tuple;

    template <typename T, typename ... Ts>
    struct are_same_in_tuple<T, std::tuple<Ts...>> : are_same<T, Ts...> {};

    template<typename ... Ts>
    struct func_trait
    {
        typedef helper_split3<std::tuple<Ts...>,
                typename make_index_sequence<sizeof...(Ts) / 3>::type,
                typename make_index_sequence<sizeof...(Ts) - 2 * (sizeof...(Ts) / 3)>::type>
                helper;

        static const bool value = (sizeof...(Ts) % 3 == 0)
                                  && are_same_in_tuple<const char*, typename helper::First>::value
                                  && are_same_in_tuple<int, typename helper::Middle>::value
                                  && are_same_in_tuple<char, typename helper::Last>::value;
    };

    template <typename ... Ts, std::size_t ... Is>
    void func(std::tuple<Ts...>&& t, index_sequence<Is...>)
    {
        constexpr std::size_t N = sizeof...(Ts);
        ::func<N>(std::array<const char*, N>({std::get<Is>(t)...}),
                  std::array<int, N>({std::get<sizeof...(Is) + Is>(t)...}),
                  std::array<char, N>({std::get<2 * sizeof...(Is) + Is>(t)...}));
    }

} // namespace detail

template <typename ... Ts>
typename std::enable_if<detail::func_trait<Ts...>::value, void>::type
func(Ts...args)
{
    detail::func(std::tie(args...), make_index_sequence<sizeof...(Ts) / 3>());
}

答案 3 :(得分:1)

在C和C ++中,在编写variadic functions时,有三大规则:

  1. 您必须始终知道第一个参数的大小和类型。
  2. 您必须能够推断出其余参数的大小和类型。
  3. 你必须知道你什么时候到达最后一个论点。
  4. 这就像printf之类的功能一样 它的第一个参数始终为char* 下一组参数由找到的%x格式器决定 当它到达最后一个Percent-specifier时,它知道它的参数。

    如果您可以遵循这三条规则,那么任何编写可变函数的在线指南都将帮助您完成您所追求的目标。

答案 4 :(得分:-1)

你不能按照你的要求这样做。你最好的选择是制作一个包含这三种类型的结构类型,然后传入可变数量的结构。