c++ - 专门化变量模板函数的问题

标签 c++ templates template-specialization variadic-functions

我正在编写一个函数 inListi(),它至少接受一个参数并将第一个参数与所有后续参数的列表进行比较。如果第一个参数 == 列表中的一个元素返回 true,否则返回 false。所以:

if( inListi( 1.2, 2.3, 4.5, 1.2))
  std::cout << "Returns true because last argument equals the first argument." << endl;

if( inListi( "hello", "world", "HEllo"))
    std::cout << "This should print out because of the last argument." << endl;

问题是,它不起作用。我有下面的代码。对于 char[N],我复制了 在继续之前将数组转换为字符串。我想这样做,因为我可能会收到一个不是 null 终止的 char[N]。

无论如何,代码如下。大多数代码都是冗余的,处理 const 和一个参数的组合是 const[N] 而另一个不是该类型。 (顺便说一句,有没有办法减少这种重复?)

#include <iostream>
#include <stdexcept>
#include <string>
#include <sstream>
#include <typeinfo>
#include <type_traits>

#include <boost/algorithm/string.hpp>

using namespace std;

////////////////////////////////////////////////////////////////////////////////
// inListi
////////////////////////////////////////////////////////////////////////////////
template<typename T>
bool inListi(T&& value)
{
    return false;
}


template<typename FirstType, typename SecondType, typename ... Rest>
bool inListi(FirstType&& first, SecondType&& second, Rest ... rest)
{
    cout << "GENERIC inListi" << endl;
    cout << "first is " << typeid(first).name() << endl;
    cout << "second is " << typeid(second).name() << endl;

    if( first == second)
        return true;
    else return inListi( first, rest...);
}


// We specialize the inListi for strings.  We lower the case.
// but what if char[n] is passed?  We have specializations that
// convert that to strings.
template<typename ... Rest>
bool inListi( string &&first, string &&second,  Rest ... rest) {
    string lFirst = first;
    string lSecond = second;

    cout << "LOWERED" << endl;

    boost::algorithm::to_lower( lFirst);
    boost::algorithm::to_lower( lSecond);

    if( lFirst == lSecond)
        return true;
    else return inListi( first, rest...);
}



// Specializations for when we are given char-arrays.  We copy the
// the arrays into a string upto the size of the array.  This is done
// to take care of the case of when the char-array is not nul-terminated.
// The amount repetition is to permutate over which argument is a char-array
// and also for const-ness.
template<int F, typename SecondType, typename ... Rest>
bool inListi( char (&&first)[F], SecondType &&second, Rest ... rest) {
    string strFirst = string( first, F);
    cout << "arr, type, rest" << endl;
    return inListi( strFirst, second, rest...);
}
template<int F, typename SecondType, typename ... Rest>
bool inListi( const char (&&first)[F], SecondType &&second, Rest ... rest) {
    string strFirst = string( first, F);
    cout << "const arr, type, rest" << endl;
    return inListi( strFirst, second, rest...);
}
template<typename FirstType, int S, typename ... Rest>
bool inListi( FirstType &&first, char (&&second)[S], Rest ... rest) {
    string strSecond = string( second, S);
    cout << "type, arr, rest" << endl;
    return inListi( first, strSecond, rest...);
}
template<typename FirstType, int S, typename ... Rest>
bool inListi( FirstType &&first, const char (&&second)[S], Rest ... rest) {
    string strSecond = string( second, S);
    cout << "type, const arr, rest" << endl;
    return inListi( first, strSecond, rest...);
}
template<int F, int S, typename ... Rest>
bool inListi( char (&&first)[F], char (&&second)[S], Rest ... rest) {
    string strFirst  = string( first, F);
    string strSecond = string( second, S);
    cout << "arr, arr, rest" << endl;
    return inListi( strFirst, strSecond, rest...);
}
template<int F, int S, typename ... Rest>
bool inListi( const char (&&first)[F], char (&&second)[S], Rest ... rest) {
    string strFirst  = string( first, F);
    string strSecond = string( second, S);
    cout << "const arr, arr, rest" << endl;
    return inListi( strFirst, strSecond, rest...);
}
template<int F, int S, typename ... Rest>
bool inListi( char (&&first)[F], const char (&&second)[S], Rest ... rest) {
    string strFirst  = string( first, F);
    string strSecond = string( second, S);
    cout << "arr, const arr, rest" << endl;
    return inListi( strFirst, strSecond, rest...);
}
template<int F, int S, typename ... Rest>
bool inListi( const char (&&first)[F], const char (&&second)[S], Rest ... rest) {
    string strFirst = string( first, F);
    string strSecond = string( second, S);
    cout << "const arr, const arr, rest" << endl;
    return inListi( strFirst, strSecond, rest...);
}


int main() {

    if( inListi( "Hello", "World", "HEllo"))
        cout << "Hello is in the listi." << endl;
    else
        cout << "Hello is not in the listi." << endl;

    return 0;
}

程序的输出如下:

[bitdiot foo]$ g++ forStackOverflow.cpp -std=gnu++0x
[bitdiot foo]$ ./a.out
GENERIC inListi
first is A6_c
second is A6_c
GENERIC inListi
first is A6_c
second is PKc
Hello is not in the listi.

注意,没有调用任何中间代码,它直接使用通用版本。此外,另一件看起来很奇怪的事情是“PKc”。我假设是 char* 类型。现在,为什么它会有不同的类型?

无论如何,谢谢!!

最佳答案

看起来您可以为 const char* 使用重载(这可能是 PKc 所指的)。

也许是这样的:

template<typename ... Rest>
bool inListi(const char *first, const char *second, Rest... rest) {
    cout << "const char*, const char*, rest" << endl;
    return inListi(string(first), string(second), rest...);
} 

另外两件事:

  • 请注意临时变量的使用,以便我们调用 string&& 版本
  • const char* 重载将匹配第一个调用,但您可能需要更多重载来处理后续调用

另一个编辑:关于 prevent array decay in parameter pack expansion 的一些很好的反馈向我们展示了如何通过在参数包上使用右值引用来完成此操作:

#include <iostream>
#include <string>
#include <utility>
#include <algorithm>
#include <cctype>

template <typename T>
bool inListi(T&& value)
{
  return false;
}

template <typename FirstType, typename SecondType, typename... Rest>
bool inListi(FirstType&& first, SecondType&& second, Rest&&... rest)
{
  if (first == second)
    return true;
  else
    return inListi(std::forward<FirstType&&>(first), rest...);
}

template <int N, int M, typename... Rest>
bool inListi(char (&first)[N], char (&second)[M], Rest&&... rest)
{
  std::string lFirst(first, N);
  std::transform(lFirst.begin(), lFirst.end(), lFirst.begin(), ::tolower);
  std::string lSecond(second, M);
  std::transform(lSecond.begin(), lSecond.end(), lSecond.begin(), ::tolower);

  if (lFirst == lSecond)
    return true;
  else
    return inListi(first, rest...);
}

template <typename... Rest>
bool inListi(const char *first, const char *second, Rest&&... rest)
{
  std::string lFirst(first);
  std::transform(lFirst.begin(), lFirst.end(), lFirst.begin(), ::tolower);
  std::string lSecond(second);
  std::transform(lSecond.begin(), lSecond.end(), lSecond.begin(), ::tolower);

  if (lFirst == lSecond)
    return true;
  else
    return inListi(first, rest...);
}

int main() {
  char a[5] = {'H','e','l','l','o'};
  char b[5] = {'W','o','r','l','d'};
  char c[5] = {'H','E','l','l','o'};
  std::cout << inListi(a, b, c) << '\n';
  std::cout << inListi("Hello", "World", "HEllo") << '\n';
  std::cout << inListi(5, 42, 5) << '\n';
}

只要所有参数都具有相同的类型,这就有效。如果您想开始混合类型,您可能需要开始编写所有适当的重载对。

关于c++ - 专门化变量模板函数的问题,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/13918400/

相关文章:

templates - 如何将PrestaShop 1.7结帐 Accordion 步骤转换为步骤进度条?

c++ - 为什么允许我显式实例化模板特化,其中默认参数不能隐式转换为其他类型?

c++ - 为什么编译器没有选择基于enable_if的特化

c++,无符号字符之间的线性插值

c++ - 返回对类数据成员的引用,然后尝试更改该成员

c++ - 从包含十进制数的字符串变量中获取值,该十进制数表示大约 64 GB 的值

c++ - 是否可以使用标签调度来确定返回类型

c++ - 从插入符获取行号

c++ - 匿名模板类型名/类声明

c++ - 错误 : template parameter redeclared here as 'false' . 一些模板魔法