Search code examples
c++templatesiteratorenable-if

Why isn't std::begin/end being considered here?


I have a template free function algorithm "contains":

template <typename collection_type, typename element_type, typename comparison_function_type>
bool contains(const collection_type & collection, const element_type & element, comparison_function_type comparison_function)
{
    using namespace ::std;
    return end(collection) != find_if(begin(collection), end(collection), [&](const element_type & candidate) { return comparison_function(candidate, element); });
}

This works for the following or fails for the following depending on what's in scope:

        static const TCHAR * kPackFiles[] = { _T("boxit"), _T("pack") };
        const auto & name = filename.GetName();
        if (contains(kPackFiles, name, Toolbox::case_insensitive_equal_to<Toolbox::TCHARStringPolicy>()))
              do_something_interesting();

The above compiles unless the following is in scope:

template <typename T>
const typename ::std::enable_if<::std::is_same<T, CStringA>::value || ::std::is_same<T, CStringW>::value, T>::type::XCHAR *
    begin(const T & str) { return str.GetString(); }

template <typename T>
const typename ::std::enable_if<::std::is_same<T, CStringA>::value || ::std::is_same<T, CStringW>::value, T>::type::XCHAR *
    end(const T & str) { return str.GetString() + str.GetLength(); }

The above is intended to extend CStringA and CStringW to offer const char_type iterators on them. This generally works for other scenarios such as for (c : my_cstring) cout << c;

But for the above case - where collection_type=const TCHAR *[2] I get messages from the compiler that it failed to specialize function template & only lists the specializations found above.

Here's the exact message:

error C2893: Failed to specialize function template 'const ::std::enable_if<std::is_same<T,CStringA>::value||std::is_same<T,CStringW>::value,T>::type::XCHAR *Toolbox::end(const T &)'

And I'm guessing that I've run up against some namespace rules here.

I'm in the midst of moving my custom library code into namespace Toolbox - including both contains template and begin and end for CStringA/W.

I've verified that if I simply don't define my CString versions of begin/end that the above code does compile. So the necessary definition of begin/end for raw arrays is include & visible.

But I'm at a loss as to why my Toolbox::contains template won't consider std::begin/end for this case - and instead tries to generate Toolbox::begin(const CString &) for kPackFiles?


Solution

  • The way using-directives work is ([namespace.udir]/2):

    A using-directive specifies that the names in the nominated namespace can be used in the scope in which the using-directive appears after the using-directive. During unqualified name lookup ([basic.lookup.unqual]), the names appear as if they were declared in the nearest enclosing namespace which contains both the using-directive and the nominated namespace. [ Note: In this context, “contains” means “contains directly or indirectly”. — end note ]

    using namespace ::std; causes the members of ::std to be visible, for this purpose, as if they were members of the global namespace. They are therefore hidden, per the normal unqualified lookup rules, by anything with the same name in the namespace Toolbox.