c++ address of an overloaded function
Asked Answered
B

1

5

I have the following small C++ code sample, but I can't figure out why the compiler works this way, although I have spent quite a lot of time studying cppreference. I would appreciate any explanations! godbolt

#include <type_traits>

template<typename Tp>
struct depend_type
{
  constexpr static bool false_ = false;
};

template<typename Tp>
struct cont
{
  using x = void;

  static_assert(std::is_same_v<Tp, int>);
  // if uncomment, will be complie error -> 'cont<Tp>::x' instantiated with 'void (*p)(int) = func;'
//   static_assert(depend_type<Tp>::false_); 
};

template<typename Tp>
void func(Tp)
{
}

template<typename Tp>
typename cont<Tp>::x func(Tp);

int main(int /* argc */, char * /*argv*/[])
{
//  func(1); is ambiguous call
  void (*p)(int) = func; // why is not ambiguous?
  return 0;
}
Biramous answered 14/12, 2023 at 16:12 Comment(9)
just to be sure, the question is about the second line while func(1) is expected to fail, right?Ligurian
I expected that there shouldn't be a compilation error in both casesSorrento
cont<Tp>::x doesn't participate in the function type deducing in void (*p)(int) = func;, is non-deduced context as always when it can't be done in 1 stage. See What is a non-deduced context?Parrish
I added static_assert to make sure 'cont<Tp>::x' is instantiated - godboltSorrento
@Ligurian Though rather I expect the same behavior for these wallpaper casesSorrento
It does not ensure the deduced context: godbolt.org/z/38PMEq7Ev. It attempted to instantiate but the deducing couldn't be done in a single stage and was discarded.Parrish
@273K do you mean "substitution" rather than "deducing" ? I mean Tp is deduced from func(Tp) and then Tp is substituted in typename cont<Tp>::xLigurian
@Ligurian Yes, I do, you describe this point better.Parrish
Short form: calling func(1) says nothing about the return type you expect from your called function (only to be thrown away, but still); your function-pointer assignment explicitly requires a function with that specific return type, and there's a template for that specific return type.Abecedary
V
7

void (*p)(int) = func; does overload resolution similarly to func(1), with one crucial difference.

Both the call and the assignment will first lookup func and find the two function templates. Template argument deduction will be done on both templates Tp = int will be deduced for both of them.

Then Tp will be substituted into the templates. This is why your static_assert(depend_type<Tp>::false_); will fire because the compiler needs to figure out what typename cont<Tp>::x is in the return type of the second function.

Then the best viable function needs to be found. For overload resolution in the call (func(1)), neither overload is better than the other (both take an argument int that came from Tp). There is nothing else to compare these two overloads, so it is ambiguous.

But in the assignment case (void (*p)(int) = func;), the return type is also considered. In the first function template, void didn't come from a template, so it is better than the second function template, where the void is a dependent type. So the first function template is chosen.

This might be easier to understand with a different example:

template<class T>
T** f() {
    std::cout << "T**\n";
    return nullptr;
}
template<class T>
T* f() {
    std::cout << "T*\n";
    return nullptr;
}
template<class T>
T f() {
    std::cout << "T\n";
    return T{};
}

int main() {
    // void** p = f<void>();  // Doesn't compile: return type doesn't participate in ranking most viable function

    // void** is a better match for T** than T* or T (it's more specialized)
    static_cast<void**(&)()>(f)();  // T**

    // void* is a better match for T* than T
    static_cast<void*(&)()>(f)();  // T*

    // T = void for the third function template is the only option
    static_cast<void(&)()>(f)();  // T
}

This is one of three scenarios where function return type is considered during overload resolution. The second being type conversion operator return types and the third during explicit (full) specialization.

Vries answered 14/12, 2023 at 17:36 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.