演算子テンプレートを条件付きでコンパイルすると、別の演算子の可用性が変わるのはなぜですか?

デボンコーンウォール

これは私の前の質問に対する一種のフォローアップ質問です(メンバー関数、フリー関数、および演算子の存在をチェックするための統一された方法)。

私はのための演算子を生成しようとしていますscalar * vectorし、vector * scalarそれらが手元にベクトルクラスによって提供されていない場合にのみ。これらの演算子が次のように存在するかどうかを確認します。

template<typename C, typename Ret, typename Arg>
struct has_operator_mult {
private:
    template<typename L, typename R, std::enable_if_t<
        std::is_convertible
        <
            decltype(std::declval<L>() * std::declval<R>()),
            Ret
        >::value
    > * = nullptr >
        static constexpr std::true_type check(nullptr_t);

    template<typename, typename>
    static constexpr std::false_type check(...);

public:
    typedef decltype(check<C, Arg>(nullptr)) type;
    static constexpr bool value = type::value;
};

Eigenライブラリ(バージョン3.3.4)のベクトルの場合、これにより次のようになります。

has_operator_mult<Vector3f, Vector3f, float>::value; // true -> Vector3f = Vector3f * float
has_operator_mult<float, Vector3f, Vector3f>::value; // true -> Vector3f = float * Vector3f

has_operator_mult<Vector3f, Vector3f, std::vector<float>>::value // false

大丈夫です。ただし、右側の乗算演算子を宣言しようとするとすぐに、次のようになります。

template<typename TVector3D,
    std::enable_if_t
    <
        !has_operator_mult<TVector3D, TVector3D, float>::value
    > * = nullptr
>
TVector3D operator*(const TVector3D & lhs, float rhs)
{
    return TVector3D{}; // implementation does not matter here
}

出力がに変わります

has_operator_mult<Vector3f, Vector3f, float>::value; // true  -> Vector3f = Vector3f * float
has_operator_mult<float, Vector3f, Vector3f>::value; // false -> Vector3f = float * Vector3f

ですから、私が自分自身を宣言していないというオペレーターは、私の存在チェックに従ってなんとか消えてしまいます。興味深いことに、これは私が使用するかどうかは変わりません

std::enable_if_t
<
    !has_operator_mult<TVector3D, TVector3D, float>::value // with negation
>

または

std::enable_if_t
<
    has_operator_mult<TVector3D, TVector3D, float>::value // without negation
>

ただし、どちらの演算子も引き続き呼び出すことができますが、Eigenライブラリの実装を呼び出します。

Vector3f v(1, 2, 3);
std::cout << v * 5.f << std::endl << std::endl;
std::cout << 5.f * v << std::endl << std::endl;

他の乗算演算子を追加すると、さらに混乱します。

template<typename TVector3D,
    std::enable_if_t
    <
        !has_operator_mult<float, TVector3D, TVector3D>::value
    > * = nullptr
>
TVector3D operator*(float lhs, const TVector3D & rhs)
{
    return TVector3D{};
}

has_operator_mult<Vector3f, Vector3f, float>::value; // false -> Vector3f = Vector3f * float
has_operator_mult<float, Vector3f, Vector3f>::value; // true  -> Vector3f = float * Vector3f

Vector3f v(1, 2, 3);
std::cout << v * 5.f << std::endl << std::endl; // calls my implementation
std::cout << 5.f * v << std::endl << std::endl; // calls Eigen implementation

std::cout << v * 5 << std::endl << std::endl; // compiler error, ambiguous function call
std::cout << 5 * v << std::endl << std::endl; // calls Eigen implementation

なんでこんなことが起こっているの?演算子の存在のチェックは間違っていますか?自分でオペレーターを宣言しない限り、うまくいくようです。

ggael

gccとclangを使用すると、コードスニペットは正常に機能しますが、MSVCでは機能しません。したがって、問題をMSVCチームに報告することをお勧めします。

そこにある3つのコンパイラを使って自分で試すことができます

この記事はインターネットから収集されたものであり、転載の際にはソースを示してください。

侵害の場合は、連絡してください[email protected]

編集
0

コメントを追加

0

関連記事

分類Dev

テンプレート化されたユーザー定義の変換演算子が戻り値の型を判別できるのはなぜですか?

分類Dev

Javaコンパイラーが三項演算子のプリミティブ型にnullをキャストできるのはなぜですか

分類Dev

AndroidStudioがいくつかのプリミティブKotlinタイプを条件付き/安全演算子として変換するのはなぜですか?

分類Dev

テンプルコピーコンストラクターを削除すると、代入演算子が機能しなくなるのはなぜですか?

分類Dev

コンパイラーが変換演算子とともに使用されたときにテンプレートパラメーターを推測できないのはなぜですか?

分類Dev

??を使用すると、コンパイラがエラー(トラップ:6)を返すのはなぜですか?辞書の演算子

分類Dev

リテラル演算子を正常にテンプレート化できないのはなぜですか?

分類Dev

次のコンテキストで条件演算子を使用すると、データ型がしつこくなるのはなぜですか?

分類Dev

>>演算子がコンパイラごとに異なる結果を表示するのはなぜですか?

分類Dev

C ++の条件演算子(?:)をコンパイル時にすることはできますか?

分類Dev

なぜswiftコンパイラはEquatableプロトコルの有無にかかわらずequality演算子で異なる動作をします

分類Dev

コンパイラが加算演算子で始まる行をボークしないのはなぜですか?

分類Dev

オーバーロードされた代入演算子を使用するとエラーが発生するのに、コンパイラが提供する演算子を使用できないのはなぜですか?

分類Dev

次の条件演算子「?:」はコンパイルされますが、実行時エラーが発生するのはなぜですか

分類Dev

'if'ステートメントと 'else if'ステートメントのオペランドタイプに一致する演算子がないとコンパイラーが通知するのはなぜですか?

分類Dev

C ++コンパイラが二項演算子とテンプレートのトークン>>をどのように区別するか

分類Dev

テンプレートパラメータがそれを提供する場合、条件付きで比較演算子のオーバーロードを提供します

分類Dev

constコンテキストでdelete演算子を使用できるのはなぜですか?

分類Dev

コピーコンストラクターや代入演算子でさえオブジェクトのコピーを許可するシングルトンデザインパターンがプライベートなのはなぜですか?

分類Dev

スコープ付き列挙型がデフォルトで演算子「<」をサポートするのはなぜですか?

分類Dev

標準ライブラリ演算子と競合することなく、関連するクラスのグループの演算子をテンプレートオーバーロードするにはどうすればよいですか?

分類Dev

リテラル演算子テンプレート:文字列を使用しないのはなぜですか?

分類Dev

条件演算子を使用するときに追加のキャストが必要なのはなぜですか?

分類Dev

Rubyで文字列を作成するときに、シャベル演算子(<<)がプラスイコール(+ =)よりも優先されるのはなぜですか?

分類Dev

なぜC ++は左側から代入演算子の右側のテンプレート引数を推定できるのですか?

分類Dev

C ++標準では、コンパイラが基本型の変換演算子の呼び出しを無視する必要があるのはなぜですか?

分類Dev

呼び出しチェーンで後続のnull条件演算子を省略できるのはなぜですか?

分類Dev

「等しくない」を使用しているときに、または演算子がifステートメントで機能しないのはなぜですか?

分類Dev

変換コンストラクターと変換演算子が定義されている場合、どちらが優先され、コンパイラーがこの変換を異なるのはなぜですか?

Related 関連記事

  1. 1

    テンプレート化されたユーザー定義の変換演算子が戻り値の型を判別できるのはなぜですか?

  2. 2

    Javaコンパイラーが三項演算子のプリミティブ型にnullをキャストできるのはなぜですか

  3. 3

    AndroidStudioがいくつかのプリミティブKotlinタイプを条件付き/安全演算子として変換するのはなぜですか?

  4. 4

    テンプルコピーコンストラクターを削除すると、代入演算子が機能しなくなるのはなぜですか?

  5. 5

    コンパイラーが変換演算子とともに使用されたときにテンプレートパラメーターを推測できないのはなぜですか?

  6. 6

    ??を使用すると、コンパイラがエラー(トラップ:6)を返すのはなぜですか?辞書の演算子

  7. 7

    リテラル演算子を正常にテンプレート化できないのはなぜですか?

  8. 8

    次のコンテキストで条件演算子を使用すると、データ型がしつこくなるのはなぜですか?

  9. 9

    >>演算子がコンパイラごとに異なる結果を表示するのはなぜですか?

  10. 10

    C ++の条件演算子(?:)をコンパイル時にすることはできますか?

  11. 11

    なぜswiftコンパイラはEquatableプロトコルの有無にかかわらずequality演算子で異なる動作をします

  12. 12

    コンパイラが加算演算子で始まる行をボークしないのはなぜですか?

  13. 13

    オーバーロードされた代入演算子を使用するとエラーが発生するのに、コンパイラが提供する演算子を使用できないのはなぜですか?

  14. 14

    次の条件演算子「?:」はコンパイルされますが、実行時エラーが発生するのはなぜですか

  15. 15

    'if'ステートメントと 'else if'ステートメントのオペランドタイプに一致する演算子がないとコンパイラーが通知するのはなぜですか?

  16. 16

    C ++コンパイラが二項演算子とテンプレートのトークン>>をどのように区別するか

  17. 17

    テンプレートパラメータがそれを提供する場合、条件付きで比較演算子のオーバーロードを提供します

  18. 18

    constコンテキストでdelete演算子を使用できるのはなぜですか?

  19. 19

    コピーコンストラクターや代入演算子でさえオブジェクトのコピーを許可するシングルトンデザインパターンがプライベートなのはなぜですか?

  20. 20

    スコープ付き列挙型がデフォルトで演算子「<」をサポートするのはなぜですか?

  21. 21

    標準ライブラリ演算子と競合することなく、関連するクラスのグループの演算子をテンプレートオーバーロードするにはどうすればよいですか?

  22. 22

    リテラル演算子テンプレート:文字列を使用しないのはなぜですか?

  23. 23

    条件演算子を使用するときに追加のキャストが必要なのはなぜですか?

  24. 24

    Rubyで文字列を作成するときに、シャベル演算子(<<)がプラスイコール(+ =)よりも優先されるのはなぜですか?

  25. 25

    なぜC ++は左側から代入演算子の右側のテンプレート引数を推定できるのですか?

  26. 26

    C ++標準では、コンパイラが基本型の変換演算子の呼び出しを無視する必要があるのはなぜですか?

  27. 27

    呼び出しチェーンで後続のnull条件演算子を省略できるのはなぜですか?

  28. 28

    「等しくない」を使用しているときに、または演算子がifステートメントで機能しないのはなぜですか?

  29. 29

    変換コンストラクターと変換演算子が定義されている場合、どちらが優先され、コンパイラーがこの変換を異なるのはなぜですか?

ホットタグ

アーカイブ