Problems specializing variable template function

早过忘川 提交于 2020-01-24 09:21:26

问题


I am writing a function inListi() which takes at least one argument and compares the first argument to thes list of all subsequent arguments. returns true if first argument == an element in the list, otherwise false. So:

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;

Problem is, it doesn't work. I have the code below. For char[N], I copy N portions of the array into a string before continuing. I want to do this, because I might be passed a char[N] that is not null terminated.

Anyways, the code is below. Most of the code is redundant and deal with const and combinations of one argument being const[N] and the other not of that type. (By the way, is there a way to reduce this repetition?)

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

The output of the program is the following:

[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.

Notice, that none of the intermediary code is called, it goes straight to using the generic version. Also, another thing that looks weird is the 'PKc'. Which I am assuming is type char*. Now, why would it have a different type?

Anyways, thanks!!


回答1:


It looks like you could use an overload for const char* (which is perhaps what the PKc refers to).

Maybe something like:

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

Two other things:

  • note the use of temporaries so that we'll call the string&& version
  • a const char* overload will match the first call, but you might need more overloads to handle subsequent calls

Another edit: some great feedback on prevent array decay in parameter pack expansion shows us how this can be done by using rvalue references on the parameter pack:

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

This works as long as all arguments have the same type. If you want to start mixing types, you'll probably need to start writing all of the appropriate pairs of overloads.



来源:https://stackoverflow.com/questions/13918400/problems-specializing-variable-template-function

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!