为什么在此上下文中无法推导出模板参数?

时间:2017-01-05 18:25:40

标签: c++ c++11 templates member-function-pointers ref-qualifier

有人能解释为什么编译器(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限定符重载的方法。如果方法被const-ness或参数类型重载,一切正常。 在这种情况下,似乎只有Clang才能推断T.

2 个答案:

答案 0 :(得分:1)

总结评论中的讨论: 对于参考限定的成员函数作为模板参数的支持对于某些编译器来说是一个相对较新的特性。但是,大多数编译器的最新版本将编译此类代码。

例如:

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

将编译:

  • gcc(从7.0.0开始)
  • Visual C++(适用于v19.10.24903.0)

产生以下输出:

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

对于那些想知道&&&是什么的人:这是@JustinTime的引用:

  

基本上,&amp;是左值反射限定符,&amp;&amp;是左值   ref-qualifier(绑定临时对象);在他的例子中,MyClass m;   m.Foo(3);会调用顶部的,而MyClass {}。Foo(3);会打电话   最底层的。它们作用于隐式对象参数;左值   ref-qualifier绑定到左值引用和rvalue ref-qualifier   绑定到右值参考(没有接受的参数)   参数作为左值引用,但让它绑定到任何一个)。注意   他们实际上并没有改变*这种类型。

答案 1 :(得分:0)

如果您希望模板绑定到不同的引用类型,则需要使用通用引用

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

这将绑定到左值或右值引用。 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)...);
}