7

Consider following program:

#include <iostream>
template <typename T>
void foo(const T* x) {
    x();
}
void bar() { std::cout<<"bar() is called\n"; }
int main() {
    foo(bar);
}

It compiles fine on clang++ & VC++ but g++ gives following compiler error (See live demo here )

main.cpp: In function 'int main()':
main.cpp:10:9: error: no matching function for call to 'foo(void (&)())'
  foo(bar);
         ^
main.cpp:3:6: note: candidate: template<class T> void foo(const T*)
 void foo(const T* x) {
      ^~~
main.cpp:3:6: note:   template argument deduction/substitution failed:
main.cpp:10:9: note:   types 'const T' and 'void()' have incompatible cv-qualifiers
  foo(bar);
         ^

I've used -pedantic-errors when using g++ & clang++ and I've used /W4 & /Zaoption when using VC++ compiler. See live demo here & here. So, I want to know how template type parameter T will be deduced here ? If I remove const from the program then it compiles fine on g++ also. If I use const T& then it compiles fine on all 3 compilers. So, how exactly type will be deduced here in these cases ?

Update:

This program fails in compilation on Intel C++ compiler also. See live demo here . So, is this bug in g++ & Intel C++ or bug in Clang++ & VC++ ?

StoryTeller - Unslander Monica
  • 165,132
  • 21
  • 377
  • 458
Destructor
  • 14,123
  • 11
  • 61
  • 126
  • 1
    The *usual* way to pass functions or predicates using templates, is as plain "values", like e.g. `template void foo(T x) { x(); }` See e.g. almost all functions in [the standard algorithm library](http://en.cppreference.com/w/cpp/algorithm) for examples. Can you perhaps elaborate on the reason you want to use a `const` pointer? – Some programmer dude Mar 15 '18 at 09:33

1 Answers1

8

This is essentially CWG issue 1584:

It is not clear whether the following is well-formed or not:

void foo(){}
template<class T>   void deduce(const T*) { }

int main() {
  deduce(foo);   
}

Implementations vary in their treatment of this example.

Which is currently still active. It's not really possible to say which compiler is right. Though as the note from 2015 indicates, the consensus in the CWG is currently that this should be rejected.


To give a little more context, we must remember that a function type with a cv-qualifier-seq has special meaning (think member functions), and is not simply a type the designates something which may not be modified. Moreover, you can't even add the cv qualification in some sneaky manner, as [dcl.fct]/7 illustrates:

The effect of a cv-qualifier-seq in a function declarator is not the same as adding cv-qualification on top of the function type. In the latter case, the cv-qualifiers are ignored. [ Note: A function type that has a cv-qualifier-seq is not a cv-qualified type; there are no cv-qualified function types. — end note ][ Example:

typedef void F();
struct S {
  const F f;        // OK: equivalent to: void f();
};

— end example ]

There is no way in the language to form a const qualified function type. And yet, the deduction we need is to have const T deduced as void(). The former is a const qualified type, and it must also be a function type. But that's a type that cannot exist! So how can it be deduced?!

On the other hand there is machinery in the standard to deduce it if you were using a reference instead of a pointer.

So it isn't that clear how this should be resolved. On the one hand the wording today doesn't allow it per-se, but on the other hand machinery for it is already in place for references. So some implementations go ahead and do the same for pointers.

Destructor
  • 14,123
  • 11
  • 61
  • 126
StoryTeller - Unslander Monica
  • 165,132
  • 21
  • 377
  • 458
  • It seems the root problem is function type has no cv-qualified type. Don't understand why... upvoted. – llllllllll Mar 15 '18 at 11:16
  • @liliscent - I tried to give an interpretation for it, as I see it. – StoryTeller - Unslander Monica Mar 15 '18 at 16:58
  • You said there is machinery in the standard to deduce it if you were using a reference instead of a pointer. Will you explain in simple way what happens when I use reference by quoting the standard. I mean how type will be deduced in case of reference ? – Destructor Mar 15 '18 at 17:14
  • @Destructor - I'm afraid mentioning there is machinery is the simplest I personally can do it. There's a bunch of special [cases for references](http://eel.is/c++draft/temp.deduct), that ultimately culminate to allowing it. Look for things like *" P is not a reference type"* or *" P is a reference type"* – StoryTeller - Unslander Monica Mar 15 '18 at 17:24
  • @Destructor I think the current question is already solved, this answer is already very informative. It would be better to ask another question, which also facilitates google search. – llllllllll Mar 15 '18 at 17:25
  • @StoryTeller: I think you are talking about [this](http://eel.is/c++draft/temp.deduct#call-4.1). right ? – Destructor Mar 15 '18 at 17:52
  • @Destructor - I'm sleepy, but that looks like it. – StoryTeller - Unslander Monica Mar 15 '18 at 18:28
  • @StoryTeller: clang++ 6.0.0 gives compiler error. See live demo [here](https://wandbox.org/permlink/mOBjjLEtzDI1BbLB). So,it seems that they behave like g++ & ICC. Right ? – Destructor Mar 20 '18 at 06:59
  • @Destructor - You test code is not equivalent to the one in the cwg. You attempt a `T()` - which is object creation. Clang complains because you can't do that with a function type. – StoryTeller - Unslander Monica Mar 20 '18 at 09:21
  • @StoryTeller: Ohh yes my mistake. Sorry, I didn't notice it. Thank you – Destructor Mar 20 '18 at 09:52