![](/img/trans.png)
[英]Why does the code work fine when I compile and run it even though I have not mentioned size of array?
[英]Why does the following code compile even though I have undefined member functions?
我在编写这段代码的过程中已经过了一半,并认为在按下构建按钮之前, 显然不会进行编译 。 我感到惊讶的是,它不仅可以编译,而且可以链接并正常工作。
如果我猜到了,我会说SFINAE负责编译...是吗?
struct BaseClass
{
public:
BaseClass() {}
template<typename T>
BaseClass(const T& a_other)
{
int i = 0; // for break point
}
template<typename T>
BaseClass& operator= (const T& a_other)
{
int i = 0; // for break point
return *this;
}
private:
BaseClass(const BaseClass& a_other); // Does not have a definition
BaseClass& operator= (const BaseClass& a_other); // Does not have a definition
};
struct MyClass : public BaseClass
{
};
int main()
{
MyClass i, j;
i = j;
return 0;
}
编辑 :我正在使用Visual C ++ 2008,也许这是VS的古怪
该代码不合法。
i = j
调用MyClass
隐式定义的副本分配运算符。 此函数为其每个子对象(包括直接基类[class.copy 12.8 p28])调用复制分配运算符。
如果将代码添加到BaseClass的副本分配运算符中,则可以看到VS出了什么问题:
template<typename T>
BaseClass& operator= (const T& a_other)
{
std::cout << typeid(T).name() << '\n';
int i = 0; // for break point
return *this;
}
对我来说,它打印出“ struct MyClass”。 VS通过直接传递MyClass:operator=
接收的参数而不是j的BaseClass
子对象来调用BaseClass
复制赋值运算符。
SFINAE没有发挥作用,因为模板功能没有失败。 VS只是错误地生成了隐式副本分配运算符。
总结一下:VS生成隐式副本赋值运算符为
MyClass &operator=(const MyClass& rhs) {
static_cast<BaseClass&>(*this).operator=(rhs);
return *this;
}
什么时候应该是:
MyClass &operator=(const MyClass& rhs) {
static_cast<BaseClass&>(*this).operator=(static_cast<const BaseClass&>(rhs));
return *this;
}
蒙上阴影:编译器使用T
= MyClass
实例化基类的operator =
。 我现在知道这是合法的还是什至是必需的,但它具有某种意义:用于operator =
的自动生成的代码本质上看起来像这样(伪代码):
MyClass& operator =(MyClass const& other) {
BaseClass::operator =(other);
return *this;
}
现在,编译器发现BaseClass::operator =<MyClass>(MyClass const&)
是最佳匹配,并将其实例化。
好吧,由于它不能调用BaseClass::operator=(const BaseClass&)
(从默认生成的MyClass::operator=
获取其值),因为它是私有的,所以只调用template<typename T> BaseClass::operator(const T &)
其中T=BaseClass
。 因此,没有对未定义函数的调用。
我猜如果其他人是公开的,情况会有所不同,在这种情况下,编译器会更喜欢模板,但是由于他在私有时看不到它们,因此模板版本匹配得很好。
编译器将自动为MyClass
生成一个(默认)副本构造函数,因为未定义。 如果将i
和j
的类型从MyClass
更改为BaseClass
,则会看到预期的错误,因为编译器随后尝试绑定私有的,未实现的赋值运算符。
使用MSVC 2010 Ultimate SP1进行更深入的研究,我们可以看到确切原因:
MyClass::operator=:
00201230 push ebp
00201231 mov ebp,esp
00201233 push ecx
00201234 mov dword ptr [ebp-4],ecx
00201237 mov eax,dword ptr [__that]
0020123A push eax
0020123B mov ecx,dword ptr [this]
0020123E call BaseClass::operator=<MyClass> (202130h)
00201243 mov eax,dword ptr [this]
00201246 mov esp,ebp
00201248 pop ebp
00201249 ret 4
BaseClass::=<T>
通过MyClass
默认复制运算符使用MyClass
作为类型来为MyClass
调用赋值运算符,这是由bug还是由MSVC决定的,取决于MS和C ++标准。
MyClass
将使用隐式定义的副本分配运算符,因为没有用户声明的版本。 根据该标准,隐式副本分配将执行以下操作(C ++ 03 12.8 / 13“复制类对象”):
请注意,在12.8 / 9中,该标准将用户声明的副本赋值运算符定义为“具有类X,X&,const X&,volatile X&或const volatile X&的一个参数的类X的非静态非模板成员函数” (强调我的)。
所以根据标准,模板功能
template<typename T>
BaseClass& operator= (const T& a_other);
MyClass
隐式副本分配运算符不应调用它。 MSVC在这里以非标准方式运行。 GCC正确地诊断了这一点:
C:\temp\test.cpp: In member function 'MyClass& MyClass::operator=(const MyClass&)':
C:\temp\test.cpp:29:14: error: 'BaseClass& BaseClass::operator=(const BaseClass&)' is private
C:\temp\test.cpp:33:8: error: within this context
C:\temp\test.cpp: In function 'int main()':
C:\temp\test.cpp:40:7: note: synthesized method 'MyClass& MyClass::operator=(const MyClass&)' first required here
声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.