Почему аргумент шаблона не может быть выведен в этом контексте?

Может ли кто-нибудь объяснить, почему компиляторы (g ++, visual c ++) не могут вывести аргумент шаблона в этом случае?

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
}

Почему компиляторы не могут вывести T в MyClass? Это происходит только для методов, перегруженных квалификаторами ref. Если метод перегружен константностью или типами параметров, все работает нормально.
Кажется, что только Clang может вывести T в этом случае.

16

Решение

Подводя итоги обсуждения в комментариях:
Поддержка ссылочных функций-членов в качестве аргументов шаблона является относительно новой функцией для некоторых компиляторов. Однако последние версии большинства компиляторов будут компилировать такой код.


Например:

#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);
}

Скомпилируем с:

  • НКУ (работает с 7.0.0)
  • Visual C ++ (работает с v19.10.24903.0)

производя следующий вывод:

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

Для тех, кому интересно, что & а также && для: вот цитата из @JustinTime:

В принципе, & является квалификатором lvalue, и && это значение
ref-квалификатор (привязывается к временному объекту); в его примере MyClass m;
m.Foo (3); вызовет верхний, а MyClass {}. Foo (3); назвал бы
нижний Они действуют на неявный параметр объекта; именующий
ref-qualifier связывается с lvalue ссылкой, а rvalue ref-qualifier
привязывается к rvalue ссылке (функции, которые не имеют ни
параметр в качестве ссылки lvalue, но пусть он привязан к любому). Обратите внимание, что
они на самом деле не меняют * этот тип.

0

Другие решения

Если вы хотите, чтобы ваш шаблон связывался с различными ссылочными типами, вам нужно использовать универсальную ссылку

template<typename T>
void func(T&& arg)
{
other_func(std::forward<T>(arg));
}

Это будет привязывать либо к ссылкам lvalue, либо к rvalue. std :: forward обеспечит использование соответствующей ссылки в последующих вызовах. Я не уверен, как вписать двойной амперсанд в ваш код, но, возможно, просто

template<typename T>
void CallFoo(void(T::*func)(int)&&)

Возможно, лучше будет

template<typename func_t>
void CallFoo(func_t && f)
{
call(std::forward<func_t>(f));
}

template<typename func_t>
void call(typename std::remove_reference<func_t> & f)
{
f();
}

template<typename func_t>
void call(typename std::remove_reference<func_t> && f)
{
f();
}

или любой другой синтаксис, который вам нужен для вызова указателя на функцию, может быть, * f ();

И если вы также хотите передать аргументы:

template<typename func_t, typename ... args_t>
void CallFoo(func_t && f, args_t && ... args)
{
call(std::forward<func_t>(f), std::forward<args_t>(args)...);
}

template<typename func_t, typename ... args_t>
void call(typename std::remove_reference<func_t> & f, args_t && ... args)
{
f(std::forward<args_t>(args)...);
}

template<typename func_t, typename ... args_t>
void call(typename std::remove_reference<func_t> && f, args_t && ... args)
{
f(std::forward<args_t>(args)...);
}
0

По вопросам рекламы [email protected]