Home > Back-end >  ADL conflicts with a function in the current namespace
ADL conflicts with a function in the current namespace

Time:05-20

I have a function in my namespace. Its name is destroy. std namespace also contains a function with this name. When I call my function within my namespace, the call is ambiguous, because std::destroy is also added to overload resolution set by argument dependent lookup.

#include <string>
#include <memory>

namespace my {

    template <typename TIter>
    void destroy(TIter it_begin, TIter it_end) noexcept
    {
        // Dummy
    }
    
    void func(std::string *p_begin, std::string *p_end)
    {
        destroy(p_begin, p_end);
    }

}

The code above fails to compile.

g   -std=c  17 -O2 -Wall -pedantic -pthread main.cpp && ./a.out
main.cpp: In function 'void my::func(std::string*, std::string*)':
main.cpp:14:16: error: call of overloaded 'destroy(std::string*&, std::string*&)' is ambiguous
   14 |         destroy(p_begin, p_end);
      |         ~~~~~~~^~~~~~~~~~~~~~~~
main.cpp:7:10: note: candidate: 'void my::destroy(TIter, TIter) [with TIter = std::__cxx11::basic_string<char>*]'
    7 |     void destroy(TIter it_begin, TIter it_end) noexcept
      |          ^~~~~~~
/usr/local/include/c  /11.2.0/bits/stl_construct.h:240:5: note: candidate: 'void std::destroy(_ForwardIterator, _ForwardIterator) [with _ForwardIterator = std::__cxx11::basic_string<char>*]'
  240 |     destroy(_ForwardIterator __first, _ForwardIterator __last)
      |     ^~~~~~~

Ok, I can explicitly specify namespace when calling this function: my::destroy(p_begin, p_end). But that's not a generic solution. Imagine I add a function void ababaken(std::string) but later a function with the same name is added to std. My code will produce compilation error again. And I do not want to explicitly specify namespace for every function call.

What else can I do to avoid ambiguity with std namespace when std function is caught up by ADL? How can I prevent ADL?

CodePudding user response:

If you don't want to decorate every call, you can:

  • Replace the function with a lambda, those are not affected by ADL.
  • Replace the namespace with a class with static member functions.

If you one a one-off solution, you can:

  • (destroy)(p_begin, p_end); Surround the function name in parentheses.

  • my::destroy(p_begin, p_end); As you said, a qualified name also works.

CodePudding user response:

You only have a conflict because both the argument and the function are declared in namespace std.

ADL doesn't cause a conflict between my::destroy and std::destroy if the argument isn't declared in namespace std. So, the following is perfectly OK:

struct foo 
{

};

namespace my {

    template <typename TIter>
    void destroy(TIter it_begin, TIter it_end) noexcept
    {
        // Dummy
    }

    struct bar
    {

    };

    void func(foo *p_begin, foo *p_end)
    {
        destroy(p_begin, p_end);
    }

    void func(bar *p_begin, bar *p_end)
    {
        destroy(p_begin, p_end);
    }
}

You specifically have a problem because you want to my::destroy on objects declared in namespace std.

This is why adding the my namespace scope resolution to the specific instance of calling destroy on std::string is the best solution.

#include <string>

namespace my {

    void func(std::string *p_begin, std::string *p_end)
    {
        my::destroy(p_begin, p_end);
    }

}

Don't worry about the future. You can't predict it. If a future version of the standard library adds a class or function with the same name as something in your code, you are likely to have more problems than just this.

And frankly, a future compiler error is a far better proposition than the compiler silently selecting the wrong function overload.

  • Related