Может ли кто-нибудь объяснить, почему компиляторы (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 в этом случае.
Подводя итоги обсуждения в комментариях:
Поддержка ссылочных функций-членов в качестве аргументов шаблона является относительно новой функцией для некоторых компиляторов. Однако последние версии большинства компиляторов будут компилировать такой код.
Например:
#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);
}
Скомпилируем с:
производя следующий вывод:
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, но пусть он привязан к любому). Обратите внимание, что
они на самом деле не меняют * этот тип.
Если вы хотите, чтобы ваш шаблон связывался с различными ссылочными типами, вам нужно использовать универсальную ссылку
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)...);
}