Home > Mobile >  How to do actions based on template typename checks?
How to do actions based on template typename checks?

Time:12-22

Just started exploring template feature for one of my task, I need to add some actions based on the typename in the template. Can someone point out what is wrong with this kind of structure:

#include <iostream>
#include <type_traits>

using namespace std;

template <typename T>
T foo()
{
    if(std::is_same<T, int>::value)
    {
        return 2;
    }
    if(std::is_same<T, std::string>::value)
    {
        return "apple";
    }
}

int main()
{
    std::cout<<"foo is: "<<foo<int>()<<std::endl;
    return 0;
}

I am wondering:

  1. Why this error is happening main.cpp:23:16: error: invalid conversion from ‘const char*’ to ‘int’ and how to get rid of it?
  2. Is there a better way to do specific actions based on typename provided to the function?

UPDATE:

It turned out my program is using below C 17 compiler

Attempt:

I tried another approach to handle this case but failed miserably:

#include <iostream>
#include <type_traits>

using namespace std;

template <typename T, typename U>
T foo()
{
    T t = U;
    return t;
}

int main()
{
    std::cout<<"foo is: "<<foo<int, 1>()<<std::endl;
    return 0;
}

Anyone can point out what is going wrong here?

CodePudding user response:

The problem is that, even if your T is int, all the branches still have to compile. So the second return statement causes an error because the string literal cannot be converted to the int return value.

Since C 17, you can use if constexpr to tell the compiler that the condition is a compile time constant which allows it to only compile the needed branches:

#include <iostream>
#include <type_traits>

using namespace std;

template <typename T>
T foo()
{
    if constexpr(std::is_same<T, int>::value)
    {
        return 2;
    }
    if constexpr(std::is_same<T, std::string>::value)
    {
        return "apple";
    }
}

int main()
{
    std::cout<<"foo is: "<<foo<int>()<<std::endl;
    return 0;
}

If you need a solution in earlier standards, you'll have to use template specialization like this:

#include <iostream>
#include <type_traits>

using namespace std;

//general template
template <typename T>
T foo();

//specialization for int
template <>
int foo<int>()
{
    return 2;
}

//specialization for string
template <>
string foo<string>()
{
    return "apple";
}

int main()
{
    std::cout<<"foo is: "<<foo<int>()<<std::endl;
    return 0;
}

CodePudding user response:

Template instantiations must be valid on all branches, which yours aren't. What I mean by that is this is how the functions generated by your template look like:

int foo<int>()
{
    if(true)
    {
        return 2;
    }
    if(false)
    {
        return "apple"; // error
    }
}

std::string foo<std::string>()
{
    if(false)
    {
        return 2; // error
    }
    if(true)
    {
        return "apple";
    }
}

Since C 17 you can use constexpr if to solve this:

template <typename T>
T foo()
{
    if constexpr (std::is_same<T, int>::value)
    {
        return 2;
    }
    else if constexpr (std::is_same<T, std::string>::value)
    {
        return "apple";
    }
}

CodePudding user response:

Another alternative to C 17's constexpr-if if you are in earlier language versions is tag dispatch:

#include <iostream>
#include <string>
#include <type_traits>

namespace detail {

template <typename T> struct Tag {};

int f_impl(Tag<int>) { return 2; }
std::string f_impl(Tag<std::string>) { return "apple"; }

} // namespace detail

template <typename T> T foo() { return detail::f_impl(detail::Tag<T>{}); }

int main() {
  std::cout << "foo is: " << foo<int>() << std::endl;
  return 0;
}

For this simple example specializations will do, but tag dispatch can be useful for more complex "if-else-dispatch" logic.

  • Related