【问题标题】:Function argument template parameter ambiguous for const ref typesconst ref 类型的函数参数模板参数不明确
【发布时间】:2015-11-10 02:21:56
【问题描述】:

我在将 const ref 参数传递给调用其他函数的模板函数时遇到问题。考虑以下代码:

struct A
{
    void foo(const int& i) { }
};

template <class ...Args> 
void a_caller(A& a, void(A::*f)(Args...), Args&& ...args)
{
    (a.*f)(std::forward<Args>(args)...);
}

int main()
{
    int i = 42;
    A a;

    a_caller(a, &A::foo, i); // (1) compiler error
    a_caller<const int&>(a, &A::foo, i); // (2) ok
}

所以,我有一个成员函数A::fooconst int&amp; 参数,我想在包装器a_caller 中调用它。第 (1) 行导致以下错误:

'void a_caller(A &,void (__thiscall A::* )(Args...),Args &&...)' : template parameter 'Args' is ambiguous
see declaration of 'a_caller'
could be 'const int&'
or       'int&'

我的第一个问题是为什么会这样?我给编译器一个非重载函数A::foo,为什么它不能从中推导出Args? 第二个问题是为什么 std::make_unique 不会发生这种情况?下面的代码在我看来是一样的,但是编译器推断构造函数参数类型没有问题:

struct A
{
    A(const int& i)  { }
};

int main()
{
    int i = 42;
    auto aptr = std::make_unique<A>(i);
}

【问题讨论】:

    标签: c++ templates c++11 forwarding-reference


    【解决方案1】:

    您正试图将Args 硬塞到履行两个不同(且不一定兼容)的角色中。第一个作用是f的参数类型。第二个是给a_caller的参数类型。

    由于实现完美转发的方式,在您的示例中传递i 想要将此iArgs 类型推断为int &amp;。然而,A::foo 中相同的Args 类型是const int &amp; 类型——因此是模棱两可的推论。

    在某种程度上,完美转发的全部意义在于,转发参数的类型是在现场推断出来的(并且通常不能再用于其他任何事情)。所以你必须做这样的事情:

    template <class ...Params, class ...Args>
    void a_caller(A& a, void(A::*f)(Params...), Args&& ...args)
    {
      (a.*f)(std::forward<Args>(args)...);
    }
    

    当参数与参数不匹配时,您将不得不依赖 f 的调用来告诉您。

    【讨论】:

      【解决方案2】:

      我的第一个问题是为什么会这样?我给编译器一个非重载函数A::foo,为什么不能从中推导出Args?

      因为您尝试对函数 a_caller 的第一个和第二个参数推导 Args 两次。而且这个推导的类型不匹配,const int&amp; 为第一个参数,int&amp; 为第二个参数。

      第二个问题是为什么 std::make_unique 不会发生这种情况?

      因为 make_unique 只是将其参数转发给类构造函数。

      我认为你的代码应该是这样的:

      #include <memory>
      
      struct A
      {
          void foo(const int& i) { }
      };
      
      template <typename F, class ...Args> 
      void a_caller(A& a, F &&f, Args&& ...args)
      {
          (a.*f)(std::forward<Args>(args)...);
      }
      
      int main()
      {
          int i = 42;
          A a;
      
          a_caller(a, &A::foo, i);
      }
      

      DEMO

      【讨论】:

      • 感谢使用方法指针作为模板参数的提示 - 看起来比 Angew 提案更短(对于方法的返回类型不重要的情况)
      【解决方案3】:

      错误信息告诉你发生了什么

      see declaration of 'a_caller'
      could be 'const int&'
      or       'int&'
      

      因此,您传递的成员函数采用const int&amp;,因此编译器将Args 推断为const int&amp;,但您也将i 传递给Args,它推断为int&amp;。这些冲突让你得到一个错误。你可以const_casti 编译,或者你可以传递一个const int 作为第二个参数

      a_caller(a, &A::foo, const_cast<const int&>(i)); 
      const int foo = 42;
      a_caller(a, &A::foo, foo);
      

      【讨论】:

        猜你喜欢
        • 2021-09-02
        • 2021-12-13
        • 2020-04-12
        • 2019-01-30
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 2017-03-22
        • 2016-07-16
        相关资源
        最近更新 更多