3

This is a continuation of my prior question. Note that the declaration void (*pf)(int) = bar; fires the static_assert. I don't understand why. Note also that if I replace barby bar<const int> in this declaration the code compiles.

#include <iostream>
#include <type_traits>

template<typename T>
void bar(T t)
{
    static_assert(std::is_same<T, const int>::value, "Error!");
    std::cout << t << '\n';
}

int main()
{
    //  static_assert doesn't fire because T==const int
    bar<const int>(1);

    //  But here static_assert fires because T==int (see the error message). Why is this?
    //  If I replace `bar` by `bar<const int>` below the code compiles.

    void(*pf)(int) = bar;
    pf(1000);
}

Live example

Community
  • 1
  • 1
Mao
  • 1,065
  • 8
  • 12
  • The compiler is deducing that `T` = `int` based on the function pointer signature provided. Changing `void(*pf)(int)` to `void(*pf)(const int)` doesn't fix it though, presumably because the const qualifier is dropped somewhere along the chain. Someone here can probably say exactly where. – dlf Jul 08 '14 at 14:38
  • 1
    Why did you expect `T` be `const int` in `void(*pf)(int) = bar` ? – Jarod42 Jul 08 '14 at 14:39
  • @Jarod42 I wasn't expecting anything. I was just trying to verify that the type of the function `bar` instantiated by the the expression `bar(1);` was `bar(int)` from my prior question. But then I realized that the compiler is instantiating another function `bar` with the declaration `void(*pf)(int) = bar;`. And this I can't understand. – Mao Jul 08 '14 at 14:45

2 Answers2

5

The behavior is quite straightforward. T is being deduced as int from the function pointer type below, hence the static_assert fails.

void(*pf)(int) = bar; // [T = int]

if I replace bar by bar<const int> in this declaration the code compiles

That's because you've now explicitly specified that T is const int, and it's no longer being deduced as int.

void(*pf)(int) = bar<const int>; // [T = const int]

You're still allowed to create a function pointer of type void(*)(int) to the function void(const int) because top level consts are not part of the function signature.

Adding const to the function pointer type doesn't help because of the same reason, the top level const in the function argument type is discarded before T is deduced, and it results in the same behavior as the first example.

void(*pf)(const int) = bar;  // [T = int]
Praetorian
  • 106,671
  • 19
  • 240
  • 328
  • So how come `void(*pf)(const int) = bar` fails? – Matt Phillips Jul 08 '14 at 14:43
  • @MattPhillips Same reason, the top level `const` is discarded before `T` is deduced. – Praetorian Jul 08 '14 at 14:45
  • "Same reason"? Ah I see you've edited your question to put in what dlf answered. – Matt Phillips Jul 08 '14 at 14:46
  • It might be worth pointing out (if that's really correct), that the type of `void(*pf)(const int)` really *is* `void(*)(int)`. – dyp Jul 08 '14 at 14:51
  • `T is being deduced as int from the function pointer type below, hence the static_assert fails.` Can you provide a quote from the Standard supporting this? – Mao Jul 08 '14 at 14:51
  • @MattPhillips By "same reason" I meant this sentence from *my* answer - *because top level consts are not part of the function signature* - which was the last sentence before I added the final paragraph to address your comment. And I didn't *edit anything in* from dlf's answer, everything but the last paragraph, aside from a couple of grammatical improvements, were present in the very first revision I submitted. – Praetorian Jul 08 '14 at 14:52
  • 1
    @ChairmanMao [dcl.fct]/5 "The type of a function is determined using the following rules. [...] After producing the list of parameter type, any top-lvel *cv-qualifiers* modifying the parameter type are deleted when forming the function type." This applies as well to function pointers. For the deduction, see [temp.deduct.funcaddr] – dyp Jul 08 '14 at 14:53
2

The compiler is deducing that T = int based on the function pointer signature provided. Changing void(*pf)(int) to void(*pf)(const int) doesn't fix it though, because cv qualifiers are dropped during type deduction (and, as dyp pointed out in the comments, when determining the type of a function). § 14.8.2.1/3 of the C++11 draft ("Deducing template arguments from a function call"):

If P is a cv-qualified type, the top level cv-qualifiers of P’s type are ignored for type deduction

To fix this, explicitly indicate the type you want:

void(*pf)(const int) = bar<const int>;

dlf
  • 9,045
  • 4
  • 32
  • 58
  • 1
    I do think it rather has to do with `void(*)(const int)` being *the same type as* `void(*)(int)`. – dyp Jul 08 '14 at 14:44
  • Yes; I assume that's why the rules say to ignore cv qualifiers here. – dlf Jul 08 '14 at 14:45
  • 1
    No, what I mean is that `std::is_same::value == true` even though there is no deduction involved. – dyp Jul 08 '14 at 14:47
  • The deduction for `void(*pf)(int) = bar;` happens according to [temp.deduct.funcaddr], which specifies it via `template void deduce( void(*)(T) );` called with an argument of type `void(*)(const int)`. Since deduction here happens according to [temp.deduct.type], you cannot just drop the `const`. Consider `template struct foo{}; template void deduce( foo );` called with an argument of type `foo`. – dyp Jul 08 '14 at 15:00