Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Overload resolution and arrays: which function should be called?

Consider the following program:

#include <cstddef> #include <cstdio>  void f(char const*&&)      { std::puts("char const*&&");      } // (1) void f(char const* const&) { std::puts("char const* const&"); } // (2)  template <std::size_t N> void f(char const (&)[N])  { std::puts("char const(&)[N]");   } // (3)  int main() {     const char data[] = "a";     f(data); } 

Which f should be called? Why?

The latest released versions of three compilers disagree on the answer to this question:

  • (1) is called when the program is compiled using g++ 4.5.2
  • (2) is called when the program is compiled using Visual C++ 2010 SP1
  • (3) is called when the program is compiled using Clang 3.0 (trunk 127530)

Have the overload resolution rules changed substantially in different C++0x drafts? Or, are two of these compilers really just completely wrong? Which overload is the correct overload to be selected per the latest C++0x draft?

like image 541
James McNellis Avatar asked Mar 18 '11 02:03

James McNellis


People also ask

What is the 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.


1 Answers

First, the conversion sequence of all three is the same, except that for the first two, there is an lvalue transformation (lvalue to rvalue conversion), which however is not used in ordering conversion sequences. All three are exact matches (the function template specialization has parameter type char const(&)[2]).

If you iterate over the rules at 13.3.3.2p3, you stop at this paragraph

S1 and S2 are reference bindings (8.5.3) and neither refers to an implicit object parameter of a non-static member function declared without a ref-qualifier, and S1 binds an rvalue reference to an rvalue and S2 binds an lvalue reference.

A conversion sequence cannot be formed if it requires binding an rvalue reference to an lvalue, the spec says at 13.3.3.1.4p3. If you look at how reference binding works at 8.5.3p5 last bullet, it will create a temporary (I think they meant rvalue temporary) of type char const* from the array lvalue and bind the reference to that temporary. Therefor, I think (1) is better than (2). Same holds for (1) against (3), although we wouldn't need this because (3) is a template so in a tie, we would choose (1) again.

In n3225, they changed the reference binding rules so that rvalue references can bind to initializer expressions that are lvalues, as long as the reference will be bound to an rvalue (possibly created by converting the initializer properly before). This could influence the handling by Visual C++, which may not be up to date here.

I'm not sure about clang. Even if it would ignore (1), then it would end up in a tie between (2) and (3), and would need to choose (2) because it's a non-template.


I think that 8.5.3p5 last bullet is confusing because it says "Otherwise a temporary of type ..". It's not clear whether the temporary is regarded as an lvalue or as an rvalue by 13.3.3.1.4p3, which means I'm not sure how the following should really behave according to the exact words of the spec

void f(int &); void f(int &&);  int main() {   int n = 0;   f(n); } 

If we assume the temporary is treated as an rvalue by clause 13, then we bind an rvalue ref to an rvalue in the second function and an lvalue in the first. Therefor, we will choose the second function and then get a diagnostic by 8.5.3p5 last bullet because T1 and T2 are reference-related. If we assume the temporary is treated as an lvalue by clause 13, then the following would not work

void f(int &&); int main() {   f(0); } 

Because we would bind an rvalue ref to an lvalue which by clause 13 will make the function non-viable. And if we interpret "binding an rvalue ref to an lvalue" to refer to the initializer expression instead of the final expression bound to, we won't accept the following

void f(float &&); int main() {   int n = 0;   f(n); } 

This however is valid as of n3225. So there seems to be some confusion - I sent a DR to the committee about this.

like image 188
Johannes Schaub - litb Avatar answered Oct 17 '22 19:10

Johannes Schaub - litb