fabian
fabian

Reputation: 1839

Type Inference for std::string in Template Function Fails

I am trying to learn c++ templates. One function in a script compiles, whilst a seemingly similar function fails. The entire script is:

#include <string>
#include <vector>
using std::vector;

template <typename T>
void works(vector<int>& vi, vector<double>& vd, vector<T>& inp) {
    if (std::is_same<T, double>::value)
        vd.push_back(inp[0]);
    else if (std::is_same<T, int>::value)
        vi.push_back(inp[0]);
}

template <typename T>
void doesnt_work(vector<std::string>& vs, vector<double>& vd, vector<T>& inp) {
    if (std::is_same<T, double>::value)
        vd.push_back(inp[0]);
    else if (std::is_same<T, std::string>::value)
        vs.push_back(inp[0]);  // Line 18:  no matching member function
}

int main() {
    vector<double> d = {2, 3, 4};
    vector<double> doubles;
    vector<std::string> strings;
    vector<int> ints;
    doesnt_work(strings, doubles, d);  // Line 26: Comment out and script runs
    works(ints, doubles, d);
    return 0;
}

The function works takes the two vectors vi and vd containing int and double as references, as well as a third vector with elements of type T. I then try to check for the type of T and push the first element of the vector inp into either vi or vd. In contrast, the function doesnt_work causes problems. Instead of accepting a vector of int as parameter, it accepts a vector of std::string. I can run the entire script when I comment out line 26. Otherwise, clang++ tells me that

error: no matching member function for call to 'push_back'
        vs.push_back(inp[0]);

Does somebody kindly have any idea what I am doing wrong? I do not understand why the program cannot access the function push_back of the vector vs.

Upvotes: 3

Views: 257

Answers (1)

Yakk - Adam Nevraumont
Yakk - Adam Nevraumont

Reputation: 275385

template <typename T>
void now_works(vector<std::string>& vs, vector<double>& vd, vector<T>& inp) {
  if constexpr (std::is_same<T, double>::value)
    vd.push_back(inp[0]);
  else if constexpr (std::is_same<T, std::string>::value)
    vs.push_back(inp[0]);  // Line 18:  no error
}

your problem is that all branches are compiled even if they aren't run.

if constexpr makes them compiled, then discards them at compile time, ignoring any type errors.

The code that worked, everything could convert even if not run, so it compiled.

The code that didn't work, the conversion you wanted to exclude is illegal, so it failed at compile time.

Note that if constexpr only works because the branching clause is a compile-time constant, and because it is in a template, and the clause is dependent on the template parameters, and the code that is invalid is also dependent on the template parameters.

If you don't have if constexpr yet because you are stuck on an old version of C++, you will be forced to do something ugly like tag dispatching or SFINAE with helper functions or something more arcane.

Upvotes: 3

Related Questions