Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Incorrect overload resolution for 2-argument functions

Let's take the following example program:

#include <cmath>

namespace half_float
{
    template<typename T> struct half_expr {};

    struct half : half_expr<half>
    {
        operator float() const;
    };

    template<typename T> half sin(const half_expr<T>&);
    template<typename T> half atan2(const half_expr<T>&, const half_expr<T>&);
}

using namespace std;
using half_float::half;

int main()
{
    half a, b;
    half s = sin(a);
    half t = atan2(a, b);
}

In VS 2010 this compiles just fine (ignore the obvious linker errors for now). But in VS 2012 this gives me:

error C2440: 'conversion' : cannot convert from 'float' to 'half_float::half'

So it seems overload resolution doesn't pick the version from namespace half_float (which ADL should accomplish), but the one from std using the implicit conversion to float. But the strange thing is, that this only happens for the atan2 call and not the sin call.

In the larger project, where this error actually first occured to me it also occurs for other 2-argument functions (or rather those with 2 half arguments), like fmod, but not for any 1-argument function. Likewise in the larger project it also works fine for gcc 4.6/4.7 and clang 3.1 without error, though I didn't test this SSCCE version explicitly there.

So my question is, is this erroneous behaviour on VS 2012's side (given that it only happens for 2012 and only for the 2-argument function), or did I oversee some subtleties in the overload resolution rules (which can indeed get a bit tricky, I guess)?

EDIT: It also happens if I'm directly using namespace half_float or put the whole thing in global namespace directly. Likewise does it also happen if I'm not using namespace std, but this is rather the VS-implementation putting the math functions in global namespace.

EDIT: It happens both with the original VC 2012 compiler as well as the November 2012 CTP of it.

EDIT: Although I'm not completely sure it is really a violation of the standard in the strictest sense, I have filed a bug for it based on the findings in my answer, since it is at least inconsistent to the definition of the 1-argument functions and deserves further investigation by the VS-Team.

like image 905
Christian Rau Avatar asked Jan 12 '13 16:01

Christian Rau


People also ask

What is overload resolution?

The process of selecting the most appropriate overloaded function or operator is called overload resolution. Suppose that f is an overloaded function name. When you call the overloaded function f() , the compiler creates a set of candidate functions.

What does overload resolution failed mean?

It generates this error message when one overload is more specific for one argument's data type while another overload is more specific for another argument's data type.

When you call an overloaded function How does compiler identify the right one?

At compile time, the compiler chooses which overload to use based on the types and number of arguments passed in by the caller. If you call print(42.0) , then the void print(double d) function is invoked. If you call print("hello world") , then the void print(std::string) overload is invoked.

How function calls are matched with overloaded functions?

The process of matching function calls to a specific overloaded function is called overload resolution. Just because there is no exact match here doesn't mean a match can't be found -- after all, a char or long can be implicitly type converted to an int or a double .


1 Answers

I think I found the cause. The C++ standard says in section 26.8 [c.math], that for the mathematical functions of the C library,

there shall be additional overloads sufficient to ensure:

  1. If any argument corresponding to a double parameter has type long double, then all arguments corresponding to double parameters are effectively cast to long double.
  2. Otherwise, if any argument corresponding to a double parameter has type double or an integer type, then all arguments corresponding to double parameters are effectively cast to double.
  3. Otherwise, all arguments corresponding to double parameters are effectively cast to float.

Which can also be seen in the atan2 documentation.

Those overloads are provided by VS 2012 through the use of a general function template of the form:

template<typename T,typename U> common_float_type<T,U>::type atan2(T, U);

So we have a template function whose instantiation would involve an implicit conversion (from half& to const half_expr<half>&) and a template function which can be instantiated directly. Thus the latter is preferred. This doesn't happen for the 1-argument functions because for those there only has to be a general version for integral arguments, which is provided by VS 2012 for only those using a std::enable_if of std::is_integral.

But I think the standard is a bit unclear about the fact that those "additional overloads" are to be provided only for builtin types. So in the end I'm still not sure if VS 2012 strictly violates the standard with its overly generic functions or if it is a viable implementation option to provide those.

EDIT: As it seems, there is already defect report 2086 for the standard's unclear wording and a fix is on it's way, limiting the requirement for those additional overloads to only arithmetic types. Since this seems to have always been the original intent (and realized by nearly all existing implementations) and it was merely the wording that was unclear, I would indeed regard this a bug in VS 2012's implementation.

like image 60
Christian Rau Avatar answered Oct 06 '22 01:10

Christian Rau