繁体   English   中英

即使我有未定义的成员函数,为什么下面的代码也会编译?

[英]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生成一个(默认)副本构造函数,因为未定义。 如果将ij的类型从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.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM