1

Suppose that I have a couple of functions to process different types of arguments. For example processInt for processing int variables and processString for processing std::string variables.

int processInt(int i) 
{
    return i;
}

string processString(string s)
{
    return s;
}

And, I have a template function called foo which takes either of int or std::string as argument. And inside this function I need to conditionally call the processInt or processString based on the variable type sent to it as the argument. The foo function would look like this:

#include <type_traits>

template<typename T>
T foo(T value)
{
    T variable;
    if (std::is_same<T, int>::value) 
    {
        variable = processInt(value); 
    }
    else if (std::is_same<T, string>::value)
    {
        variable = processString(value);
    }
    return variable;
}

int main() {
    string s = "Abc";
    int i = 123;
    cout << foo(s) << " " << foo(i) << endl;     
}

However, with the above foo function I get the following errors:


error: no matching function for call to 'processInt'
                variable = processInt(value); 
                           ^~~~~~~~~~
note: in instantiation of function template specialization 'foo<std::__cxx11::basic_string<char> >' requested here
    cout << foo(s) << " " << foo(i) << endl;    
            ^
note: candidate function not viable: no known conversion from 'std::__cxx11::basic_string<char>' to 'int' for 1st argument
int processInt(int i) 
    ^
error: no matching function for call to 'processString'
                variable = processString(value);
                           ^~~~~~~~~~~~~
note: in instantiation of function template specialization 'foo<int>' requested here
    cout << foo(s) << " " << foo(i) << endl;    
                             ^
note: candidate function not viable: no known conversion from 'int' to 'std::__cxx11::string' (aka 'basic_string<char>') for 1st argument
string processString(string s)
       ^

Source code: https://godbolt.org/z/qro8991ds

How could I do this correctly to conditionally call functions based on type of the template parameter in generic functions?

EDIT

I like to use a single foo function without overloading or specializations as otherwise there may be some code duplications. The foo function may have lot of lines. But, the difference between code for int and string will be a single line.

Tharindu Sathischandra
  • 1,654
  • 1
  • 15
  • 37

3 Answers3

6

For something like this I would suggest templates and specialization:

// Declare base template
template<typename T>
T process(T);

// Specialization for integers
template<>
int process(int value)
{
    // ... code to process integers...
}

// Specialization for strings
template<>
std::string process(std::string value)
{
    // ... code to process strings...
}

Then the foo function simply becomes

template<typename T>
T foo(T value)
{
    return process(value);
}

Optionally plain old overloading should work just as well.

Some programmer dude
  • 400,186
  • 35
  • 402
  • 621
1

In C++17, you can use immediately-invoked lambda and if constexpr to do this

template<typename T>
T foo(T value) {
  auto variable = [&value]{
    if constexpr (std::is_same_v<T, int>)
      return processInt(value);
    else if constexpr (std::is_same_v<T, std::string>)
      return processString(value);
  }();

  // use variable

  return variable;
}

Demo

康桓瑋
  • 33,481
  • 5
  • 40
  • 90
1

All branches should be valid, even if branch is not taken. C++17 has if constexpr which would solve your issue

template<typename T>
T foo(T value)
{
    T variable;
    if constexpr (std::is_same<T, int>::value) 
    {
        variable = processInt(value); 
    }
    else if constexpr (std::is_same<T, string>::value)
    {
        variable = processString(value);
    }
    return variable;
}

For pre-c++17, you might use overload to obtain similar results (tag dispatching for more complex cases):

int foo(int value)
{
    return processInt(value);
}

std::string foo(const std::string& value)
{
    return processString(value);
}

template <typename T
    // possibly some SFINAE to allow some conversion with above functions
    /*,  std::enable_if_t<std::is_constructible<std::string, T>, bool> = true */>
T foo(T value)
{
    return T{};
}
Jarod42
  • 203,559
  • 14
  • 181
  • 302