4

SFINAE を使用して、特定の名前空間に関数が存在するかどうかを確認したいと考えています。私はSFINAE が仕事をする別の名前空間からの自由な関数をテストすることを発見しましたが、私が理解していないことがいくつかあります。

現在、リンクされた質問から直接、この作業コードがあります:

// switch to 0 to test the other case
#define ENABLE_FOO_BAR 1

namespace foo {
  #if ENABLE_FOO_BAR
    int bar();
  #endif
}

namespace detail_overload {
  template<typename... Args> void bar(Args&&...);
}
namespace detail {
  using namespace detail_overload;
  using namespace foo;
  template<typename T> decltype(bar()) test(T);
  template<typename> void test(...);
}
static constexpr bool has_foo_bar = std::is_same<decltype(detail::test<int>(0)), int>::value;

static_assert(has_foo_bar == ENABLE_FOO_BAR, "something went wrong");

(ENABLE_FOO_BARマクロはテスト目的のためだけのものです。私の実際のコードでは、そのようなマクロを利用できません。それ以外の場合は、SFINAE を使用しません)


detail_overload::bar()ただし、他の名前空間を配置するとすぐに(必要に応じてusingディレクティブを調整します)、検出は静かに中断static_assertされ、when foo::bar()exists が開始されます。「ダミー」bar()オーバーロードがグローバル名前空間または名前空間の一部に直接ある場合にのみ機能します (グローバルスコープ::detail_overloadに注意してください)。::

// breaks
namespace feature_test {
  namespace detail_overload {
    template<typename... Args> void bar(Args&&...);
  }
  namespace detail {
    using namespace detail_overload;
    using namespace foo;
    //...

// breaks
namespace feature_test {
  template<typename... Args> void bar(Args&&...);
  namespace detail {
    using namespace foo;
    //...

// breaks
namespace detail {
  namespace detail_overload {
    template<typename... Args> void bar(Args&&...);
  }
  using namespace detail_overload;
  using namespace foo;
  //...

// works
template<typename... Args> void bar(Args&&...);
namespace feature_test {
  namespace detail {
    using namespace foo;
    //...

// works
namespace detail_overload {
  template<typename... Args> void bar(Args&&...);
}
namespace feature_test {
  namespace detail {
    using namespace detail_overload;
    using namespace foo;
    //...

これは私がリンクした質問とまったく同じ問題であり、前述のようにすでに実用的な解決策があることを認識していますが、対処されていないのはなぜこれが正確に起こるのですか?

副次的な質問として、どちらかbar()または名前空間でグローバル名前空間を汚染することなく、正しい SFINAE 検出を実現する方法はありdetail_overloadますか? feature_test動作しない例から推測できるように、すべてを 1 つの名前空間にきちんとまとめたいと思います。

4

2 に答える 2

3

のフォールバック宣言がbarテンプレート (= 短いコード) にならないように少し変更します。これは純粋に名前検索の問題であるため、SFINAE は使用しません。

namespace foo {
    int bar(int);
}

namespace feature_test {
    namespace detail_overload {
        void bar(...);
    }

    namespace detail {
        using namespace detail_overload;
        using namespace foo;

        void test() { bar(0); } // (A)
    }
}

行 (A) で、コンパイラは name を見つける必要がありますbar。それはどのように調べられますか?引数に依存しないため、非修飾ルックアップである必要があります: [basic.lookup.unqual]/2

using ディレクティブによって指定された名前空間からの宣言は、 using ディレクティブを囲む名前空間で可視になります。7.3.4 を参照。3.4.1 で説明されている非修飾名ルックアップ規則のために、using ディレクティブによって指定された名前空間からの宣言は、その囲んでいる名前空間のメンバーと見なされます。

それらは、囲んでいる名前空間ではなく囲んでいる名前空間になることに注意してください。[namespace.udir]/2 からの詳細は、問題を明らかにします:

[...] 非修飾名のルックアップ (3.4.1) では、名前は、 using ディレクティブと指定された名前空間の両方を含む最も近い外側の名前空間で宣言されているかのように表示されます。

つまり、barinsideの名前検索の場合test:

namespace foo {
    int bar(int);
}

// as if
using foo::bar;
namespace feature_test {
    namespace detail_overload {
        void bar(...);
    }

    // as if
    using detail_overload::bar;
    namespace detail {
        // resolved
        // using namespace detail_overload;
        // using namespace foo;

        void test() { bar(0); } // (A)
    }
}

したがって、 にある名前は、グローバル スコープにある (ない) 名前barを隠します。feature_test

注:引数に依存する名前検索 (および 2 番目の SFINAE) を使用して、この問題を回避できる可能性があります。何か思いついたら追記します。

于 2013-09-21T15:32:46.390 に答える
2

DyPの回答と彼のコメントに加えて:

関数barが引数を取る場合、従属名ルックアップを使用して機能させることができます ( の 2 番目のオーバーロードなしbar)。

実際、私の実際のコードbar() では引数を取ります。

副次的な質問として、グローバル名前空間を汚染することなく、正しい SFINAE 検出を達成する方法はありますか...

そうです、従属名の検索は魅力のように機能します。完全を期すために、また将来他の人に役立つ場合に備えて、現在完全に機能するコードを次に示します。

#define ENABLE_FOO_BAR 1

namespace foo {
  #if ENABLE_FOO_BAR
    int bar(int);
  #endif
}

namespace feature_test {
  namespace detail {
    using namespace foo;
    template<typename T> decltype(bar(std::declval<T>())) test(int);
    template<typename> void test(...);
  }
  static constexpr bool has_foo_bar = std::is_same<decltype(detail::test<int>(0)), int>::value;
  static_assert(has_foo_bar == ENABLE_FOO_BAR, "something went wrong");
}

すべての功績はDyPにあります。私は自分でこれについて考えたとは思いません。

于 2013-09-21T16:53:19.003 に答える