8

私はこのコードの作業の途中で、ビルド ボタンを押す前にこれは明らかにコンパイルされないだろうと考えました。コンパイルだけでなく、リンクも機能することに驚きました。

私が推測すると、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の奇妙な癖かもしれません

4

5 に答える 5

3

コードは合法ではありません。

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 は、jのサブオブジェクトだけでなく、BaseClass受け取ったパラメーターを直接渡すことによって、コピー代入演算子を呼び出しています。MyClass:operator=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;
}
于 2012-02-27T22:20:06.360 に答える
1

暗闇で撮影: コンパイラは、基本クラスoperator =をインスタンス化しますT= MyClass. これが合法なのか、それとも必須なのかはわかりましたが、ある種の意味がありoperator =ます。

MyClass& operator =(MyClass const& other) {
    BaseClass::operator =(other);
    return *this;
}

これで、コンパイラはそれBaseClass::operator =<MyClass>(MyClass const&)が最適であると判断し、それをインスタンス化します。

于 2012-02-27T21:40:59.790 に答える
0

MyClassユーザーが宣言したバージョンがないため、暗黙的に定義されたコピー代入演算子を使用します。標準によると、その暗黙的なコピー代入は次のことを実行します (C++03 12.8/13 "クラス オブジェクトのコピー")。

  • 各サブオブジェクトは、その型に適した方法で割り当てられます。 — サブオブジェクトがクラス型の場合、クラスのコピー代入演算子が使用されます (明示的な修飾によるかのように。つまり、より派生したクラスで可能な仮想オーバーライド関数は無視されます)。 );

12.8/9 では、標準はユーザー宣言のコピー代入演算子を「型 X、X&、const X&、volatile X& または const volatile X& のパラメーターを 1 つだけ持つ、クラス 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 
于 2012-02-28T06:46:08.077 に答える
0

ええと、それはプライベートであるため(その価値のためにBaseClass::operator=(const BaseClass&)生成されたデフォルトから)呼び出すことができないため、単に. したがって、未定義の関数への呼び出しはありません。MyClass::operator=template<typename T> BaseClass::operator(const T &)T=BaseClass

他のバージョンがパブリックの場合は状況が異なると思います。その場合、コンパイラはテンプレートよりもそれらを優先しますが、プライベートの場合はそれらを見ることができないため、テンプレートのバージョンも同様に一致します。

于 2012-02-27T21:26:32.220 に答える
0

コンパイラは、(デフォルトの) コピー コンストラクターMyClassが定義されていないため、自動生成します。iandjの型を から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によって呼び出されます。これがバグなのか、MSVC イズムなのかは、MS と C++ 標準によって決定されます。BaseClass::=<T>MyClassMyClass

于 2012-02-27T21:27:16.613 に答える