RedX
RedX

Reputation: 15175

Why is the template trying to instanciate with 'int&' instead of 'int'?

I am trying to write a simple function that will get me a number from user input within a range.

When instanciating this function i explicitly tell it i want it instanciated with int but still i get the error:

thermo.cpp:105:31: error: no matching function for call to ‘getInput(int&)’

Why is is trying to find a function that takes int& as argument?

template<class T, T min = std::numeric_limits<T>::min, T max = std::numeric_limits<T>::max>
T getInput(T default_value = T()){
  std::string input;
  T myNumber = T(); //default inits
  while(true){
    getline(cin, input);

    if(input.length() == 0){
      return default_value;
    }

    // This code converts from string to number safely.
    stringstream myStream(input);
    if (myStream >> myNumber){
      if(myNumber > max || myNumber < min){
        stringstream ss;
        ss << "Input out of bounds. Received " << myNumber << " expected between " << min << " and " << max << ".";
        throw invalid_argument(ss.str());
      }
      return myNumber;
    }

    cout << "Invalid number, please try again" << endl;
  }
}

void get(const std::string& prompt, int& param){
  cout << prompt << " [" << param << "]:";
  param = getInput<int,0>(param); // i specifically tell it i want 'int', why am i getting 'int&'?
}

Update

If i try CharlesB suggestion:

void get(const std::string& prompt, int& param){
  cout << prompt << " [" << param << "]:";
  param = getInput<int,0>(int(param));
}

i get

thermo.cpp:105:36: error: no matching function for call to ‘getInput(int)’

Forgot:

g++ 4.5.3 under cygwin

Command line:

$ g++ thermo.cpp -o thermo.exe -Wall -pedantic -std=c++0x

Update 2

if i call it like this

void get(const std::string& prompt, int& param){
  cout << prompt << " [" << param << "]:";
  param = getInput<int,0,15>(int(param)); // fully parameterized
}

it works... but i'd rather not specify an upper bound (not even numeric_limits) on each call.

Upvotes: 2

Views: 261

Answers (4)

The error code does not mean what you think. The error code is a shorthand for:

no matching function call to getInput that takes an int modifiable lvalue expression as the single argument

Where int modifiable lvalue expression is the type of the expression that you are using to make the call in this case param. Now the problem is that output of error codes in this format is that it is very verbose and it would become very hard to read with just two or three arguments of non trivial types, so the compiler condenses the error report and tells you:

no matching function call to getInput(int&), note that here int& is not the type of the function that will be called, as the compiler was unable to find such a function, but rather it is the type of the argument that is being used in the call.

If you perform the change that CharlesB suggests, then you will get a different error message saying that it cannot find getInput(int). The difference here is that int(param) creates a temporary (rvalue expression), so the error now reflects it. The need for a different error code comes from the fact that if you had a getInput(int&) function, in this second case, that overload cannot be used.

On the reason why you are getting that error code the basic problem is that std::numeric_limits<T>::max is not of type T. Your problem is the very base of SFINAE: You have defined a template that takes as second and third arguments T, and that T should be initialized with std::numeric_limits<T>::min (and max). Now when the compiler tries to determine the function to call, it will find that template, use T for int (you provided the exact type), 0 for the min and will then try to infer the last argument. At this point it will try to get a T value (last template argument) through the default template argument by substituting the known template arguments in: std::numeric_limits<T>::max. The problem is that std::numeric_limits<int>::max is not an int, but rather a static member function, so the types don't match, yielding a substitution failure. The language determins that substitution failure is not an error (SFINAE) and it only means that this template will be removed from the list of candidates for the function call. Because there is no other matching overload, the compiler gives up and tells you that it could not find a matching function for the call.

In C++11 you can use std::numeric_limits<T>::max(), as the function is marked as a const_expr and can thus be called to obtain a constant expression of type T that can be used as the template argument, but if you are working with a C++03 compiler, you will need to work around the problem in a different way, like moving the min and max to default arguments to a function, or providing different overloads that take values from the user or will call a function (default to std::numeric_limist<T>::max if the argument is not present, but this latter option is more cumbersome.

Upvotes: 2

mfontanini
mfontanini

Reputation: 21900

Don't use templates for min and max:

template<class T>
T getInput(T default_value = T(), T min = std::numeric_limits<T>::min(), T max = std::numeric_limits<T>::max());

There is no reason to use templates for those arguments(besides the fact that it does not work).

Edit: You can't use those arguments as template values since std::numeric_limits<T>::min() is a function, its value is known on runtime, and template value arguments have to be bound to a value at compile time. This is valid:

template<class T, T min = 0, T max = 5>
T getInput(T default_value);

Since 0 and 5 are known during compilation.

Upvotes: 3

luke
luke

Reputation: 37453

I don't know if this is the issue, but I can't imagine it's helping. This line:

template<class T, T min = std::numeric_limits<T>::min, T max = std::numeric_limits<T>::max>

...is using min/max as values, when they're really functions. Maybe that's confusing the template parameters?

Upvotes: 2

CharlesB
CharlesB

Reputation: 90316

Template functions are instanciated with argument type, and param is a int&.

Rather do

 param = getInput(int(param));

Also min and max can't be template arguments, a template argument is class, a typename or a POD.

Upvotes: 1

Related Questions