【问题标题】:Perfect forwarding and ambiguity of function-parameter binding函数参数绑定的完美转发和歧义
【发布时间】:2013-09-18 19:20:37
【问题描述】:

我正在试验 C++11 的完美转发功能。 Gnu g++ 编译器报告函数参数绑定的歧义问题(错误显示在下面的源代码之后)。我的问题是为什么会这样,按照函数参数绑定过程,我没有看到歧义。我的推理如下:在 main() 中调用 tf(a) 绑定到 tf(int&) 因为 a 是一个左值。然后函数 tf 将左值引用 int& a 转发给函数 g 因此函数 void g(int &a) 应该被唯一地调用。因此,我看不出模棱两可的原因。当从代码中删除重载函数 g(int a) 时,错误消失。这很奇怪,因为 g(int a) 不能成为与 int &a 绑定的候选对象。

这是我的代码:

void g(int &&a)
{
  a+=30;
}

void g(int &a)
{
  a+=10;
}

void g(int a)   //existence of this function originates the ambiguity issue
{
  a+=20;
}

template<typename T>
void tf(T&& a)
{
  g(forward<T>(a));;
}

int main()
{
  int a=5;
  tf(a);
  cout<<a<<endl;
}

编译g++ -std=c++11 perfectForwarding.cpp报告以下错误

perfectForwarding.cpp: In instantiation of ‘void tf(T&&) [with T = int&]’:
perfectForwarding.cpp:35:7:   required from here
perfectForwarding.cpp:24:3: error: call of overloaded ‘g(int&)’ is ambiguous
perfectForwarding.cpp:24:3: note: candidates are:
perfectForwarding.cpp:6:6: note: void g(int&&) <near match>
perfectForwarding.cpp:6:6: note:   no known conversion for argument 1 from ‘int’ to ‘int&&’
perfectForwarding.cpp:11:6: note: void g(int&)
perfectForwarding.cpp:16:6: note: void g(int)

【问题讨论】:

    标签: c++ c++11


    【解决方案1】:

    这很奇怪,因为 g(int a) 不能成为与 int &a 绑定的候选对象。

    那不是真的。如果您删除 g(int&amp;) 重载,则将调用 g(int)。当两者都被声明时,它是模棱两可的,因为两者都是可行的候选者并且不需要转换。

    【讨论】:

      【解决方案2】:

      Jonathan Wakelyanswer 之上添加。

      首先,问题与完美转发无关,我们可以将tf从图片中去掉。

      暂时只考虑这段代码:

      void g(int) {}
      
      int main() {
          int a = 5;       // a is an lvalue
          g(a);            // ok
          g(std::move(a)); // std::move(a) casts a to an rvalue and this call is also ok
      }
      

      这说明了一个按值接受参数的函数可以同时接受左值和右值。

      现在假设我们添加

      void g(int &) {}
      

      然后第一个调用g(a); 变得模棱两可,因为g(int &amp;) 可以采用非const 左值,没有别的。第二次调用,g(std::move(a)) 仍然可以,仍然调用g(int),因为g(int &amp;) 不能接受右值。

      现在将g(int &amp;) 替换为g(int &amp;&amp;)。后一个函数只能采用非const 右值。因此调用g(a) 可以,调用g(int)。但是,g(std::move(a)) 现在是模棱两可的。

      此时很明显,如果我们将三个重载放在一起,那么两个调用就会变得模棱两可。实际上,没有理由拥有三个重载。根据T 的类型,大多数情况下我们都有

      1. g(T)
      2. g(T&amp;)
      3. g(const T&amp;)
      4. g(const T&amp;)g(T&amp;&amp;)

      【讨论】:

        猜你喜欢
        • 2013-07-15
        • 1970-01-01
        • 2019-01-16
        • 1970-01-01
        • 1970-01-01
        • 2019-02-04
        • 2016-07-22
        • 1970-01-01
        • 2015-08-26
        相关资源
        最近更新 更多