6

コンパイルを防ぐことができない望ましくない C スタイルのキャストがあります。望ましくないキャストは、あるクラスのオブジェクトから他のクラスの非 const 参照への C スタイルのキャストを実行します。クラスは無関係です。同時に、同じクラスのオブジェクトから const 参照への C スタイルのキャストをサポートしたいと考えています。望ましいキャストをサポートするために、パブリック変換演算子を提供しています。この場合、望ましくないキャストを防ぐことはできないようです。
非 const 参照へのキャストはビルドに失敗し ( "Sandbox::B::operator Sandbox::A &() " (30 行目で宣言) はアクセスできません*)、const 参照へのキャストは失敗します(エラー: "Sandbox::B" から "const Sandbox::A" への複数の変換関数が適用されます: function "Sandbox::B::operator const Sandbox::A &()" function "Sandbox::B:: operator Sandbox::A &()" ):

#include <iostream>
#include <string>
#include <cstdlib>

namespace Sandbox {
    class A {
    public:
        A (int i) : _x (i) { }
    private:
        int _x;
    };

    class B {
    public:
        B (const char* m) : _m (m), _a (std::atoi (m)) { }

        /*
         * This one shall be supported.
         */ 
        operator const A& () {
            return _a;
        }
    private:
        /*
         * This one shall be not supported.
         * If this one is disabled both desired and undesired conversions pass the compilation.
         */ 
        operator A& ();

        const std::string _m;
        const A _a;
    };
}

int main () {
    Sandbox::A a (1973);
    Sandbox::B b ("1984");

    /*
     * This is the undesirable cast and it shall fail to compile.
     */
    (Sandbox::A&)b;
    /*
     * This is the desirable cast and it shall pass the compilation.
     */
    (const Sandbox::A&)b;

    return 0;
}

演算子を無効にするoperator A& ()と、必要な変換と不要な変換の両方がビルドされます。

gcc、icc、および MSVC コンパイルを使用しています。クライアント コードを制御できず、C スタイルのキャストを使用できません。

4

1 に答える 1

3

これでうまくいくはずです(clang3.5でテスト済み):

#include <iostream>
#include <string>
#include <cstdlib>

namespace Sandbox {
  class A {
  public:
    A (int i) : _x (i) { }

    void        fun()
    {
      std::cout << "action" << std::endl;
    }

  private:
    int _x;
  };

  class B {
  public:
    B (const char* m) : _m (m), _a (std::atoi (m)) { }

    /*
     * This one shall be supported.
     */
    template<typename T, typename Enable = typename std::enable_if<std::is_same<T, A>::value, A>::type>
    operator const T& ()
    {
      return _a;
    }

    /*
     * This one shall be not supported.
     * If this one is disabled both desired and undesired conversions pass the compilation.
     */
  private:
    template<typename T, typename Enable = typename std::enable_if<std::is_same<T, A>::value, A>::type>
    operator T& ();

    const std::string _m;
    const A _a;
  };
}

int main () {
  Sandbox::A a (1973);
  Sandbox::B b ("1984");

  /*
   * This is the undesirable cast and it shall fail to compile.
   */
  (Sandbox::A&)b;

  /*
   * This is the desirable cast and it shall pass the compilation.
   */
  (const Sandbox::A&)b;

  return 0;
}

あなたのバージョンがあなたが望むことをしない理由については、C-Style キャストのルールに関連しています:

C スタイルのキャスト式が検出されると、コンパイラは次のキャスト式をこの順序で試行します。

a) const_cast(式)

b) 拡張付きの static_cast(expression): 基本クラスにアクセスできない (つまり、このキャストが無視する) 場合でも、派生クラスへのポインターまたは参照は、明確な基本クラスへのポインターまたは参照 (およびその逆) にキャストすることが追加で許可されます。プライベート継承指定子)。明確な非仮想ベースのメンバーへのポインターからメンバーへのポインターのキャストにも同じことが適用されます

c) static_cast (拡張機能付き) の後に const_cast が続く

d) reinterpret_cast(式)

e) reinterpret_cast に続いて const_cast

コンパイルできない場合でも、それぞれのキャスト演算子の要件を満たす最初の選択肢が選択されます

免責事項: この説明は主に推測に基づいています。複数のステップと複雑なルールがあるため、理解したと思うようにすべてが実際に機能するかどうかはわかりませんが、どうぞ。

参照にキャストするので、は型エイリアスのルールにreinterpret_cast基づいて常に機能するため、その C スタイルのキャストを失敗させる唯一の方法は、その型の を明確にエラーにすることです。残念ながら、変換規則では、ユーザー定義の型への変換が、cv 修飾されていない型へのユーザー定義の変換よりも適切であるとは見なされていないようです。ターゲット型が修飾されていても、それらは両方とも同じレベルにあります。一方、テンプレートでは、SFINAE とパラメーター推定が有効になり、マウンテン ドラゴンから抽出された魔法のコンパイラー パウダーを使用すると、機能します。(ええ、このステップは私にとってももう少し神秘的です)。static_castconststatic_castconst

于 2014-10-20T15:27:24.283 に答える