C++ 为什么即使我有未定义的成员函数,下面的代码仍要编译?

C++ 为什么即使我有未定义的成员函数,下面的代码仍要编译?,c++,sfinae,C++,Sfinae,我在编写这段代码的过程中,认为在点击build按钮之前,显然不会编译这段代码。我感到惊讶的是,它不仅编译了,而且还链接并工作 如果我猜的话,我会说SFINAE负责它的编译。。。是吗 struct BaseClass { public: BaseClass() {} template<typename T> BaseClass(const T& a_other) { int i = 0; // for break point } templa

我在编写这段代码的过程中,认为在点击build按钮之前,显然不会编译这段代码。我感到惊讶的是,它不仅编译了,而且还链接并工作

如果我猜的话,我会说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;
}
struct基类
{
公众:
基类(){}
模板
基类(常数T&a_其他)
{
int i=0;//表示断点
}
模板
基类和运算符=(常量T和a_其他)
{
int i=0;//表示断点
归还*这个;
}
私人:
BaseClass(const BaseClass&a_other);//没有定义
基类和运算符=(const BaseClass&a_other);//没有定义
};
结构MyClass:公共基类
{
};
int main()
{
MyClass i,j;
i=j;
返回0;
}

EDIT:我使用的是Visual C++2008,可能这是VS的一个奇怪的怪癖,因为它不能调用
BaseClass::operator=(const BaseClass&)
(根据默认生成的
MyClass::operator=
,因为它是私有的,所以它只调用
模板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 
MyClass::operator=:
00201230推式ebp
00201231 mov ebp,esp
00201233推动ecx
00201234 mov德沃德ptr[ebp-4],ecx
00201237 mov eax,dword ptr[\uu该]
0020123A推送式eax
0020123B mov ecx,德沃德ptr[本文件]
0020123E调用基类::运算符=(202130h)
00201243 mov eax,德沃德ptr[本]
00201246 mov esp,ebp
00201248波普ebp
00201249 ret 4

BaseClass::=
使用
MyClass
作为类型,通过
MyClass
调用
MyClass
赋值运算符作为默认复制运算符,无论是bug还是MSVC ISM,都是由MS和C++标准决定的。

< P>在黑暗中:编译器用“代码> t>代码> =<代码> MyClass < /代码>实例化基类'<代码>运算符=< /COD>。我现在知道这是合法的还是必需的,但它有一定的意义:自动生成的
操作符=
代码基本上是这样的(好吧,伪代码):

现在,编译器发现
BaseClass::operator=(MyClass const&)
是最佳匹配,并将其实例化。

该代码是不合法的

i=j
调用
MyClass
中隐式定义的复制赋值运算符。此函数为其每个子对象(包括直接基类[class.copy 12.8 p28])调用复制赋值运算符

如果向基类的复制赋值运算符添加代码,则可以看到VS出错的地方:

  template<typename T>
  BaseClass& operator= (const T& a_other)
  {
    std::cout << typeid(T).name() << '\n';
    int i = 0; // for break point
    return *this;
  }
何时应该:

MyClass &operator=(const MyClass& rhs) {
    static_cast<BaseClass&>(*this).operator=(static_cast<const BaseClass&>(rhs));
    return *this;
}
MyClass和运算符=(常量MyClass和rhs){
static_cast(*this).operator=(static_cast(rhs));
归还*这个;
}

MyClass
将使用隐式定义的复制分配运算符,因为没有用户声明的版本。根据标准,该隐式复制赋值将执行以下操作(C++03 12.8/13“复制类对象”):

  • 每个子对象都以适合其类型的方式指定: -如果子对象是类类型,则使用该类的复制赋值运算符(如同通过显式限定;即忽略更多派生类中任何可能的虚拟重写函数)
请注意,在12.8/9中,该标准将用户声明的复制赋值运算符定义为“类X的非静态非模板成员函数,其中只有一个类型为X、X&、常量X&、volatile X&、或常量volatile X&”(emphasis mine)的参数

因此,根据标准,模板功能

  template<typename T>
  BaseClass& operator= (const T& a_other);

你具体问的是哪一部分?有模板函数,还有未定义的复制运算符和赋值运算符,我可以想象有人会期望这些运算符中的任何一个出现某种故障。@GregHewgill:Line
I=j
。。。它不应该简单地告诉我赋值运算符是私有的吗?我认为这个问题是相关的:。这不是在gcc 4.6.1、启用C++11的情况下编译的:错误是:在函数“int main()”中。。。已删除函数“MyClass&MyClass::operator=(const MyClass&)MyClass&MyClass::operator=(const MyClass&)”的使用被隐式删除,因为默认定义的格式可能不正确:|基类&基类::operator=(const BaseClass&)是私有的|它不能编译。如果是的话,那就是VC++的错误。但是我可以打断
基类
操作符=()
(因此对断点的注释),至少在Visual C++中是这样。如果它生成一个默认值,那么为什么要调用基赋值运算符?但是默认生成的复制构造函数调用基类复制构造函数…@Samaursa:如果您实际跟踪设置的断点,您会看到它返回到自动生成的函数
MyClass::operator=:
(至少这是我在MSVC 2010下得到的)。是的,我看到了相同的行为。但是它将导致基类过载,而不是给出错误,因为我正在尝试访问私有赋值
MyClass &operator=(const MyClass& rhs) {
    static_cast<BaseClass&>(*this).operator=(static_cast<const BaseClass&>(rhs));
    return *this;
}
  template<typename T>
  BaseClass& operator= (const T& a_other);
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