7

The following code was compiled on g++ 4.1.2 and g++ 4.4.4. Both give the results noted in the comments.

int f(const int * a)
{
   return 0;
}

template<typename A>
int f(A a)
{
   return 1;
}

int main()
{
    int x;
    // return f(&x); // returns 1
    return f((const int *)&x); // returns 0
}

It appears to boil down to a call of f(int *) resolves to f<int *>(int *) instead of the expected f(const int *). I found this shocking and completely unintuitive.

Is this a bug in g++, a dark corner of C++, or obvious for some reason I am missing? If it's not a bug, what's the theory or logic behind it? Are there any safe practices regarding this issue?

Roland
  • 381
  • 2
  • 4
  • 5
  • `f(int)` and `f(const int)` are identical prototypes as far as ANSI c++ compiler is concerned – sehe Jun 23 '11 at 23:37
  • 1
    Right, but `f(int *)` is not the same as `f(const int *)/f(int const *)` – Roland Jun 23 '11 at 23:39
  • Perhaps. Link: http://stackoverflow.com/questions/2121525/const-pointers-in-overload-resolution/2121616#2121616 – sehe Jun 23 '11 at 23:40
  • 2
    +1 for providing a minimal, complete example program. See http://sscce.org for reasons why that's a good idea. – Robᵩ Jun 23 '11 at 23:50
  • 1
    @sehe "Perhaps"? I think you are misreading the discussion at that link. "foo(int *const a)" accepts a const-pointer-to-int. In the example here, "f(const int * a)" accepts a pointer-to-const-int. – Mike C Jun 24 '11 at 00:35

2 Answers2

8

For the instantiated template f<int *> no conversion (int *->const int *) is needed, so it's a better match - actually, it's an exact match, that would lose only against a non-templated exact match, which is what happens for the second call.

The full explanation of the "better match" rules is available at §13.3.3 of the C++ standard.

Matteo Italia
  • 123,740
  • 17
  • 206
  • 299
4

Well, why are you calling the const version of the function "expected" in case of f(&x) call?

The argument type is int *, as you already know. So the f(int *) version of the function is a better match than f(const int *) version, because in the former the argument type matches exactly. The compiler sees the opportunity to generate f(int *) from the template and it takes that opportunity. That's just how it works in C++.

In cases when the template version is as good as the non-template one, the non-template one normally wins. But in this case, where the template version is obviously better, the template version wins.

Apparently you expected the compiler to choose the non-template version of the function. Why?

AnT stands with Russia
  • 312,472
  • 42
  • 525
  • 765
  • I expected it to win due to a trivial and implicit const conversion. It just seems sane to me that const upgrading a pointer type would always occur before considering templates. Perhaps there's an example as to why that would not be a good idea. – Roland Jun 24 '11 at 00:16
  • 4
    @Roland : No conversion > trivial conversion in overload resolution. – ildjarn Jun 24 '11 at 00:40
  • @Roland: imagine that you have two ways to perform an operation. It is destructive (with regard to its argument). In the `const` version, you therefore need to perform a copy of the argument first, which is obviously less efficient. Example: `T&& operator+(T&&, T const&)` is more efficient than `T operator+(T const&, T const&)`. – Matthieu M. Jun 24 '11 at 06:37
  • In that case I agree simply because those are both templates. I simply meant that I expected implicit const conversion before considering any templates at all. – Roland Jun 24 '11 at 12:04