SFINAE: checking the existence of a function breaks when the overload is moved to other namespaces

前端 未结 2 785
予麋鹿
予麋鹿 2020-12-19 17:53

I want to check for the existence of a function in a specific namespace using SFINAE. I have found SFINAE to test a free function from another namespace which does the job,

相关标签:
2条回答
  • 2020-12-19 18:19

    In addition to DyP's answer and following his comment:

    If your function bar took any arguments, you could make use of dependent name lookup to make it work (w/o a second overload of bar).

    Indeed in my real code bar() does take arguments.

    As a side question, is there any way to achieve correct SFINAE detection without polluting the global namespace...

    So yes, dependent name lookup works like a charm. For the sake of completeness, and in case it can help others in the future, here's my now perfectly working code:

    #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");
    }
    

    All credit goes to DyP, I don't believe I'd have thought about this by myself.

    0 讨论(0)
  • 2020-12-19 18:24

    I'll change it slightly so the fall-back declaration of bar isn't a template (= shorter code), and don't use SFINAE as this is purely a name lookup issue.

    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)
        }
    }
    

    In line (A), the compiler needs to find the name bar. How is it looked up? It's not argument-dependent, so it must be unqualified lookup: [basic.lookup.unqual]/2

    The declarations from the namespace nominated by a using-directive become visible in a namespace enclosing the using-directive; see 7.3.4. For the purpose of the unqualified name lookup rules described in 3.4.1, the declarations from the namespace nominated by the using-directive are considered members of that enclosing namespace.

    Note they become in an enclosing namespace, not the enclosing namespace. The details from [namespace.udir]/2 reveal the issue:

    [...] During unqualified name lookup (3.4.1), the names appear as if they were declared in the nearest enclosing namespace which contains both the using-directive and the nominated namespace.

    That is, for the name lookup of bar inside 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)
        }
    }
    

    Therefore, the name bar found in feature_test hides the name (not) found in the global scope.

    Note: Maybe you can hack around this issue with argument-dependent name lookup (and a second SFINAE). If something comes to my mind, I'll add it.

    0 讨论(0)
提交回复
热议问题