我正在使用转发构造函数创建一个瘦派生类。 (请耐心等待,我必须使用缺少继承构造函数的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>&&)
答案 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毕竟是错误的。