Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

template argument deduction for function pointer (g++ & ICC vs Clang++ & VC++ )

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++ ?

like image 363
Destructor Avatar asked Mar 15 '18 09:03

Destructor


People also ask

What is template argument deduction?

Class Template Argument Deduction (CTAD) is a C++17 Core Language feature that reduces code verbosity. C++17's Standard Library also supports CTAD, so after upgrading your toolset, you can take advantage of this new feature when using STL types like std::pair and std::vector.

What is template argument in C++?

In C++ this can be achieved using template parameters. A template parameter is a special kind of parameter that can be used to pass a type as argument: just like regular function parameters can be used to pass values to a function, template parameters allow to pass also types to a function.

CAN default arguments be used with the template?

You cannot give default arguments to the same template parameters in different declarations in the same scope. The compiler will not allow the following example: template<class T = char> class X; template<class T = char> class X { };

What is the correct syntax of defining function template template functions?

6. What is the correct syntax of defining function template/template functions? Explanation: Starts with keyword template and then <class VAR>, then use VAR as type anywhere in the function below.


1 Answers

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.

like image 62
StoryTeller - Unslander Monica Avatar answered Sep 20 '22 21:09

StoryTeller - Unslander Monica