【问题标题】:Why template argument cannot be deduced in this context?为什么在这种情况下不能推导出模板参数?
【发布时间】:2017-05-20 09:54:47
【问题描述】:

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

【问题讨论】:

  • 好吧,clang accepts it happily. 可能here 所涉及的问题对于 gcc 或 vc++ 仍未解决?
  • @jaggedSpire 我越看越困惑。我不确定它是否真的对函数类型有贡献,或者只是描述了*this 的类型。
  • @NathanOliver [dcl.fct]/6: "返回类型、参数类型列表、ref-qualifier 和 cv-qualifier-seq ... 是函数的一部分类型。”和clang treats it as part of the type
  • g++7 似乎可以编译这个。
  • @NathanOliver 基本上,&amp; 是左值引用限定符,&amp;&amp; 是右值引用限定符(绑定到临时对象); in his example, MyClass m; m.Foo(3); would call the top one, while MyClass{}.Foo(3); would call the bottom one。它们作用于隐式对象参数; lvalue ref-qualifier 绑定到 lvalue 引用,rvalue ref-qualifier 绑定到 rvalue 引用(既没有将参数作为左值引用但让它绑定到任何一个的函数)。请注意,它们实际上并没有改变 *this 的类型。

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


【解决方案1】:

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


例如:

#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 &&

对于那些想知道&amp;&amp;&amp; 是做什么的人:这是@JustinTime 的引述:

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

【讨论】:

    【解决方案2】:

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

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

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 2012-09-15
      • 2020-11-29
      • 1970-01-01
      • 2013-04-30
      • 1970-01-01
      • 2018-09-08
      • 2018-07-11
      • 1970-01-01
      相关资源
      最近更新 更多