为什么这个operator = call不明确?

时间:2016-05-13 19:46:44

标签: c++ c++11 implicit-conversion explicit

我正在使用转发构造函数创建一个瘦派生类。 (请耐心等待,我必须使用缺少继承构造函数的GCC 4.7.2。)

首次尝试时,我忘记添加explicit关键字并收到错误消息。有人可以解释为什么会发生这种特殊错误吗?我无法弄清楚事件的顺序。

#include <memory>

template<typename T>
struct shared_ptr : std::shared_ptr<T>
{
  template<typename...Args>
  /*explicit*/ shared_ptr(Args &&... args)
    : std::shared_ptr<T>(std::forward<Args>(args)...)
  {}
};

struct A {};

struct ConvertsToPtr
{
  shared_ptr<A> ptr = shared_ptr<A>(new A());
  operator shared_ptr<A> const &() const { return ptr; }
};

int main()
{
  shared_ptr<A> ptr;
  ptr = ConvertsToPtr(); // error here
  return 0;
}

错误:

test.cpp: In function ‘int main()’:
test.cpp:28:23: error: ambiguous overload for ‘operator=’ in ‘ptr = ConvertsToPtr()’
test.cpp:28:23: note: candidates are:
test.cpp:9:8: note: shared_ptr<A>& shared_ptr<A>::operator=(const shared_ptr<A>&)
test.cpp:9:8: note: shared_ptr<A>& shared_ptr<A>::operator=(shared_ptr<A>&&)

1 个答案:

答案 0 :(得分:7)

g++ 4.8.4的情况也是如此:
g++ -g -pedantic --std=c++11 -o test main.cpp
VS2015设置均为默认设置。

问题是编译器尝试将ConvertsToPtr()返回的临时对象转换为shared_ptr对象。当编译器与explicit关键字一起使用时,使用构造函数永远不会发生此转换。但是,在使用gdb进行检查时,似乎正在使用shared_ptr<A> const &()转换函数来匹配相应的类型。然后,此转换返回const shared_ptr &,在调用赋值运算符时没有歧义(这也与wojciech Frohmberg的结果相符)。

但是,如果省略explicit,则返回shared_ptr的对象。这可以与赋值运算符的rvalue版本或const lvalue版本匹配。

根据N4296,表-11,我们在使用conversion constructor构建后,有rvalue of shared_ptr个对象。但是,重载决策会找到两个匹配项,这两个匹配项都排在Exact Match之下(右边值版本为Identity matching,而另一个位于Qualification matching之下)。

我也检查了VS2015,并在评论中说明,它确实有效。但是使用一些cout调试可以看到 const lvalue赋值 rvalue优先于 rvalue const lvalue refrence版本对应物。

编辑:我在标准中看得更深,并添加了修改。关于结果VS2015的已删除文字是错误的,因为我没有定义这两项作业。当两个赋值都被声明时,它确实更喜欢rvalue。

我假设VS编译器将Identity与排名中的Qualification匹配区分开来。但是,正如我总结的那样,VS编译器是错误的。 g++编译器遵循给定的标准。但是,由于GCC 5.0可以作为Visual studio工作,编译器错误的可能性很小,所以我很乐意看到另一位专家的见解。

编辑:在13.3.3.2中,其中一个抽奖破坏者,在我写的关于它的更好的排名后,是:

  

- S1和S2是参考绑定(8.5.3),两者都不是指   声明的非静态成员函数的隐式对象参数   没有ref-qualifier,S1将rvalue引用绑定到rvalue   和S2绑定左值参考。

附加了一个示例,显示给定的右值(不是右值参考)应该与const int &&匹配const int &。因此,我猜,即使我们有&&类型而非const &&类型,也可以安全地假设它与我们的案例相关。毕竟我认为GCC 4.7,4.8毕竟是错误的。