3

I've read existing questions about this standard conversion. However, I didn't find a satisfying answer.

I have this piece of code which shows that the T* overload is chosen over the T&& one. From what I understood, the forwarding reference overload should bind everything, unless another overload is a perfect match.

In the following code, tab is a char const(&)[4].

Could someone explain to me why the array-to-pointer conversion is performed here? And if there is a way to workaround that conversion, I'm all ears!

(coliru link)

#include <utility>

template <typename T>
void f(T&& lol)
{
}

template <typename T>
void f(T* pof)
{
  static_assert(sizeof(T) && false, "");
}

template <typename T>
struct S;

int main(int argc, char *argv[])
{
  decltype("lol") tab = "lol";
  S<decltype(tab)> s;
  f("lol");
  return 0;
}
Kerrek SB
  • 464,522
  • 92
  • 875
  • 1,084
Dante
  • 404
  • 2
  • 10

2 Answers2

5

Arrays are deduced to Ptr types.

see http://en.cppreference.com/w/cpp/language/template_argument_deduction

Before deduction begins, the following adjustments to P and A are made:

1) If P is not a reference type,

a) if A is an array type, A is replaced by the pointer type obtained from array-to-pointer conversion;

b) otherwise, if A is a function type, A is replaced by the pointer type obtained from function-to-pointer conversion;

c)otherwise, if A is a cv-qualified type, the top-level cv-qualifiers are ignored for deduction

Hayt
  • 5,210
  • 30
  • 37
  • Weird thing is, even after adding a `T const(&)[N]` overload, it fails to compile, saying the call is ambiguous. Is there no way to differentiate between pointers and array at all? – Dante Aug 30 '16 at 12:47
  • You can try this: http://stackoverflow.com/questions/28182838/is-it-possible-to-overload-a-function-that-can-tell-a-fixed-array-from-a-pointer – Hayt Aug 30 '16 at 12:51
  • Well, both proposed workarounds work. Very tricky but that suits me – Dante Aug 30 '16 at 12:54
  • This is misleading. In the OP's example, in one of the alternatives `P` *is* a reference type, meaning that for that alternative the array is NOT deduced as pointer type. That's the essence of the question. – AnT stands with Russia Mar 30 '22 at 04:09
  • @AnT the compiler chooses 1 of the 2 template overloads. In the example there is only one template instantiation and that is a an array. So this means the compiler thinks the pointer overload is the more specific one. Which seems to work as intended by the standard. And if there is anything misleading: I basically just quoted cppreference. If there is something misleading there you should talk with them or the c++ standard committee. If I missed something fundamental here (which can happen) I am always eager to learn new things. You could also just provide your own better answer too. – Hayt Apr 05 '22 at 09:06
3

A single-argument function template f is considered more specialized than a template g if an arbitrary type corresponding to the argument of f can be deduced to the argument of g, but not vice versa.

An arbitrary pointer type T* can be deduced to T&&, but this does not hold in the reverse direction (T&& cannot be deduced to T* in the general case), so f(T*) is considered more specialized than f(T&&).

To resolve this, we have to make f(T*) less attractive to the compiler, for example using SFINAE:

template <typename U,
    typename T = std::enable_if_t<std::is_pointer<U>::value, std::remove_pointer_t<U>>>
void f(U const& pof)  // U == T*
ecatmur
  • 152,476
  • 27
  • 293
  • 366