Search code examples
c++templates

Return the correct type when the template passed into a function argument changes type


NOTE: Please do NOT suggest I implement overloaded functions! I know how to do that. The point of this exercise is to learn how to work with templates.

I have this function (it takes some params and returns a vector of a templated type). Because the vector contains a templated type, and this can change depending on the caller, the push_back() method needs to take the correct type.

Therefore, I have tried something like this:

template<class T>void foo(int n, vector<T>& vec)
{
   ...
   if (is_same<T, string>::value)
      vec.push_back("");
   else
      vec.push_back(INT_MIN);
}

I get a compile error in VS C++:

no overloaded function could convert all argument types

This is because, depending on what type the template is, one of these branches has the wrong type (I understand that).

However, what I do not understand is why does the compiler evaluate this at compile time? To test if the branch actually works (i.e. if the is_same statement evaluates properly and the if works), I have replaced the push_back() statement with a neutral one: int j=0;. I have compiled it, run it, and it works just fine; in other words, based on what type the template is, the first or the second branch is evaluated.

Thus, my ultimate question: how can I write something similar (that actually compiles and runs) to the if statement that if the template type is string it does one thing, and if the type is int it does something else? Or, if this is indeed the only way to do this, what flags can I turn on in the compiler to suppress the compile error (since the if statement works as expected, and it will never hit the incorrect push_back type)?


Solution

  • Since C++17, you can use constexpr if:

    template <class T>
    void foo(int n, std::vector<T>& vec) {
        if constexpr (std::is_same_v<T, std::string>) {
            vec.push_back("");
        } else if constexpr (std::is_same_v<T, int>) {
            vec.push_back(INT_MIN);
        } else {
            /*...*/
        }
    }