尽管使用std :: is_same测试它是一个std :: string,但无法将typename从int转换为std :: string

时间:2017-10-23 00:00:07

标签: c++

这是我第一次使用模板,所以我怀疑我使用不正确。

在我的代码中,我多次从控制台获得每行的空格分隔值列表。有时我希望列表是一对std::strings或一个std::string和一个int - 您明白了。所以我想创建一个类型模糊的函数,只需引用列表即可立即处理所有函数。

以下工作正常:

template<typename A, typename B> void getInputList(std::vector<std::pair<A, B>> &list) {
    //redacted while loop
    A a;
    B b;
    if ((ss >> a >> b) && ss.eof())
        list.push_back(std::make_pair(a, b));
    else {
        std::cout << "Bad values ignored" << ((list.size() != 0) ? ", previous values still in buffer.\n" : ".\n");
        std::cin.clear();
        std::cin.ignore(std::numeric_limits<std::streamsize>::max(), '\n');
    }
}

但是,如果类型为std::stringint,我输入:

  

56 56

代码会接受它,我希望字符串只是alphas。那么我添加了一个测试,看看其中一个输入是std::string,如果是,请确保它没有数字:

 template<typename A, typename B> void getInputList(std::vector<std::pair<A, B>> &list) {
    //redacted while loop
    A a;
    B b;
    if ((ss >> a >> b) && ss.eof()) {
        if (std::is_same<A, std::string>::value)
            if(std::all_of(a.begin(), a.end(), ::isdigit))
                continue;
        if (std::is_same<B, std::string>::value) 
            if(std::all_of(b.begin(), b.end(), ::isdigit))
                continue;
        list.push_back(std::make_pair(a, b));
        continue;
    }
    //more redacted code
}

这会产生以下错误,但b a不会引发任何错误:

  '.begin'左边的

必须有class / struct / union
  '。end'的左边必须有class / struct / union

我创建了一个临时函数来查看原因,而a的类型为Ab的类型为int

bool isString(std::string in) {
    return std::all_of(in.begin(), in.end(), ::isdigit);
}
template<typename A, typename B> void getInputList(std::vector<std::pair<A, B>> &list) {
//redacted while loop
    A a;
    B b;
    if ((ss >> a >> b) && ss.eof()) {
       if (std::is_same<A, std::string>::value)
          if(isString(a))
            continue;
       if (std::is_same<B, std::string>::value) 
          if(isString(b))
            continue;
       list.push_back(std::make_pair(a, b));
       continue;
   }
   //more redacted code
}

给出了:

  

无法从'int'转换为'std :: string'

如果我同时应用std::to_string,则a会产生错误:

  

'std :: to_string':9个重载中没有一个可以转换所有参数类型

因为它应该是A类型......

我做错了什么?

2 个答案:

答案 0 :(得分:2)

在大多数情况下,函数模板特化中的所有语句必须有效,即使它们永远不会被执行。

如果您可以使用C ++ 17,它会引入&#34; if constexpr&#34;解决此问题:当您键入if constexpr而不是if时,必须可以在编译时评估条件,并且可能无法对给定的模板参数集执行任何受控语句没有用这些模板参数进行实例化。

如果您不能使用C ++ 17,您可以派遣帮助函数进行验证。关键是根据类型是std::string还是其他东西来调用不同的函数。

// A string must not be all digits:
inline bool validateInput(const std::string& s) {
    return !std::all_of(s.begin(), s.end(), ::isdigit);
}
// Any other type is always valid:
template <typename T>
bool validateInput(const T&) { return true; }

template<typename A, typename B>
void getInputList(std::vector<std::pair<A, B>> &list) {
    // ...
    while (something()) {
        A a;
        B b;
        if ((ss >> a >> b) && ss.eof()) {
            if (!validateInput(a)) continue;
            if (!validateInput(b)) continue;
            list.push_back(std::make_pair(a, b));
            continue;
        }
        // ...
    }
}

答案 1 :(得分:1)

问题是您的类型检查(std::is_same<A, std::string>::value)是运行时测试。 所以编译器不知道它是否真实,然后继续。 然后使用模板化类型变量调用带有std::string参数的函数。当您的模板为int(或其他任何内容)时,这会导致类型错误。

要解决此问题,我建议您使用模板专业化:

// base template, used if no specialization matches
template<typename T>
bool stringContainingDigits(T in) {
  // no string
  return false;
}

// string specialization only used if string
template<>
bool stringContainingDigits<std::string>(std::string in) {
  // true if only contains digits
  return std::all_of(in.begin(), in.end(), ::isdigit);
}

template<typename A, typename B>
void getInputList(std::vector<std::pair<A, B>> &list) {
  //redacted while loop
  A a;
  B b;
  if ((ss >> a >> b) /* && ss.eof() */) {
    if(stringContainingDigits(a))
      continue;
    if(stringContainingDigits(b))
      continue;
    list.push_back(std::make_pair(a, b));
    continue
  }
  //more redacted code
}

顺便说一句,如果你在一个循环中有if语句,继续从同一个流ss读取,你可能宁愿省略EOF测试,因为它只适用于最后一个项目