Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Why template argument cannot be deduced in this context?

Could anyone explain why compilers (g++, visual c++) fail to deduce the template argument in this case?

struct MyClass
{
    void Foo(int x)&  {}
    void Foo(int x)&& {}
};

template<typename T>
void CallFoo(void(T::*func)(int)&)
{
    //create instance and call func
}

int main()
{
   CallFoo(&MyClass::Foo); // Fails to deduce T
}

Why compilers cannot deduce T to MyClass? This happens only for methods overloaded by ref qualifiers. If a method is overloaded by const-ness or parameter types, everything works fine. It seems that only Clang can deduce T in this case.

like image 564
John Avatar asked Jan 05 '17 18:01

John


People also ask

What is template argument deduction in C++?

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 a template argument?

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 argument be used with template class?

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 { };


1 Answers

Summarizing discussion in the comments: the support for reference-qualified member functions as template arguments is a relatively new feature for some compilers. However, the latest versions of most compilers will compile such code.


For example:

#include <iostream>

struct MyClass
{
    void Foo(int) const &
    {
        std::cout << "calling: void Foo(int) const &\n";
    }
    void Foo(int) const &&
    {
        std::cout << "calling: void Foo(int) const &&\n";
    }
};

template<typename T>
void CallFoo_lvalue(void (T::*foo)(int) const &)
{
    T temp;
    (temp.*foo)(0);
}

template<typename T>
void CallFoo_rvalue(void (T::*foo)(int) const &&)
{
    (T{}.*foo)(0);
}

int main()
{
   CallFoo_lvalue(&MyClass::Foo);
   CallFoo_rvalue(&MyClass::Foo);
}

Will compile with:

  • gcc (works from 7.0.0)
  • Visual C++ (works with v19.10.24903.0)

producing the following output:

calling: void Foo(int) const &
calling: void Foo(int) const &&

For those who are wondering what & and && are for: here's the quote from @JustinTime:

Basically, & is the lvalue ref-qualifier, and && is the rvalue ref-qualifier (binds to temporary object); in his example, MyClass m; m.Foo(3); would call the top one, while MyClass{}.Foo(3); would call the bottom one. They act on the implicit object parameter; lvalue ref-qualifier binds to lvalue reference, and rvalue ref-qualifier binds to rvalue reference (functions that have neither take the parameter as lvalue reference, but let it bind to either). Note that they don't actually change *this's type.

like image 127
AMA Avatar answered Oct 05 '22 18:10

AMA