使用约束类型在c ++中添加变量函数

时间:2017-03-30 01:46:51

标签: c++ c++11 variadic-functions

我的目标是只需添加任何数量的参数,只要它们属于以下类型 -

  1. 整数(例如123)

  2. 字符串表示为整数(例如" 123")

  3. 如果它们属于其他类型,我会忽略它们。

    方法

    1. 我使用可变函数方法。
    2. 在函数中,我检查类型。如果类型是 int ,那么我递归添加。否则,我会忽略这个论点,并重复进一步的争论。
    3. 这就是我认为代码的样子 -

      // BASE
      template <typename T>
      int func(T t)
      {
          string type= typeid(t).name();
          if (type==typeid(int).name())
              return stoi(t);
          else if (type==typeid(const char*).name())
              return atoi(t);
          else
              return 0;
      }
      // RECUR
      template<typename T, typename... Args>
      int func(T t, Args... args) // recursive variadic function
      {
          string type = typeid(t).name();
          if (type==typeid(int).name()){
              int sum = t;
              return sum+func(args...);
          }
          else
              return func(args...);
      
      }
      
      // MAIN
      int main()
      {
          // All testing here in MAIN.
      
      
          // [2]
          int funcres = func('a',1, 2.5000,"123");
          cout << funcres << endl;
      
          return 0;
      }
      

      这给了我预期的答案: 124。

      然而, 我做了以下观察,告诉我我的代码不是故障安全的。

      • 为什么在这个基函数的行中需要stoi?

        if(type == typeid(int).name())             return stoi(t);

      如果我不这样做并只调用return t,则在调用我的函数时会出错。

      Cannot initialize return object of type 'int' with an lvalue of type 'const char *'
      

      当我已经指定在类型为整数时返回整数时,这对我没有意义。

      • 即使我做return stoi(t)(我不明白为什么一开始就需要这样做),如果类型为return atoi(t),则const char*然后插入&#34;日本&#34;或&#34; 123&#34;模板arg列表[for.e.g func(1,2,2.5000,"123",12);]中的开头位于中间导致代码在此时进行投诉。

        int sum = t;

      错误与上述相同。

      Cannot initialize return object of type 'int' with an lvalue of type 'const char *'
      
      • 使用可变函数是最好的方法还是有替代方法?
      • 如果这是最好的方式(对我来说似乎是这样,因为我需要考虑添加任何数量的参数和任何类型的参数),我做错了什么?

2 个答案:

答案 0 :(得分:2)

C ++模板是静态解析的,这意味着替换参数必须适用于所有替换,甚至是那些在运行时无法访问的替换。但是,通过使用带辅助函数的重载而不是RTTI,我们可以更清晰地处理转换:

template<class T>
int forceInt(T arg) { return 0; }

int forceInt(int arg) { return arg; }

int forceInt(std::string arg) { return std::stoi(arg); }

int forceInt(const char * arg) { return std::stoi(arg); }

使用此辅助函数,您可以执行简单的递归求和:

int func() { return 0; }

template<typename T, typename... Args>
int func(T t, Args... args) // recursive variadic function
{

        return forceInt(t) + func(args...);
}

这也可以扩展为处理任何整数类型。通过在一般重载上使用SFINAE将其限制为非整数类型,这会导致int重载成为整数类型的首选。但是char是不可变的,所以如果你不想将它隐式转换为int,我们还需要添加0的char重载:

template<class T, class U = typename std::enable_if<!std::is_integral<T>::value>::type>
int forceInt(T arg) { return 0; }

int forceInt(char arg) {return 0;}

答案 1 :(得分:1)

重载是一种可能性。你也可以用一些模板魔术来做到这一点。这样做的好处是,您在求和的列表将在编译时从所有不兼容的类型中删除(除了最后一个元素,如果不匹配则替换为0)。

#include <cassert>
#include <string>
#include <type_traits>


template < typename T >
struct is_const_char : std::false_type {};
template < >
struct is_const_char < const char * > : std::true_type {};

template < typename T >
struct is_int : std::false_type {};
template < >
struct is_int < int > : std::true_type {};


// Break condition
template < typename T >
typename std::enable_if<is_int<T>::value, int>::type
sum(T t)
{
  return t;
}

template < typename T >
typename std::enable_if<is_const_char<T>::value, int>::type
sum(T t)
{
  return std::stoi(t);
}

template < typename T >
typename std::enable_if<!is_int<T>::value && !is_const_char<T>::value, int>::type
sum(T)
{
  return 0;
}


// Forward declarations
template < typename T, typename ... Args >
typename std::enable_if<is_const_char<T>::value, int>::type
sum(T, Args ...);

template < typename T, typename ... Args >
typename std::enable_if<is_int<T>::value, int>::type
sum(T, Args ...);

// Recursions
template < typename T, typename ... Args >
typename std::enable_if<!is_int<T>::value && !is_const_char<T>::value, int>::type
sum(T, Args ... args)
{
  return sum(args...);
}

template < typename T, typename ... Args >
typename std::enable_if<is_int<T>::value, int>::type
sum(T t, Args ... args)
{
  return t + sum(args...);
}

template < typename T, typename ... Args >
typename std::enable_if<is_const_char<T>::value, int>::type
sum(T t, Args ... args)
{
  return std::stoi(t) + sum(args...);
}


// Test it
int main()
{
  assert( sum('a', 1, 2, 3, "123", 4, 5) == 138 );
  assert( sum('a',1, 2.5000,"123") == 124 );
}