16

谁能解释为什么编译器(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。

4

2 回答 2

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 的引述:

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

于 2017-01-11T19:46:43.007 回答
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)...);
}
于 2018-09-11T21:26:38.023 回答