4

As a learning exercise I've been reimplementing some of the STL algorithm. Even though I've not added any using directives or using declarations for the std namespace my test code won't compile unless I explicitly prefix those functions shadowing std names.

I assume that this is due to argument dependent lookup bringing in functions from the std namespace when I pass std::vector iterators as parameters to my functions.

A small program to illustrate my problem:

#include <vector>
#include <algorithm>

namespace danstd {
template <typename I, typename T>
I find(I b, I e, T val) {
    for (; b != e; ++b) {
        if (*b == val)
            return b;
    }
    return e;
}   
}

using namespace danstd;

int main() {
    std::vector<int> v = {1, 2, 3};

    auto i = find(begin(v), end(v), 3);
    return i == end(v) ? -1 : *i;
}

When I compile, I see these error messages:

$ g++ -Wall foo.cpp
foo.cpp: In function ‘int main()’:
foo.cpp:16:37: error: call of overloaded ‘find(std::vector<int>::iterator, std::vector<int>::iterator, int)’ is ambiguous
     return *find(begin(v), end(v), 3);
                                     ^
foo.cpp:5:3: note: candidate: I find(I, I, T) [with I = __gnu_cxx::__normal_iterator<int*, std::vector<int> >; T = int]
 I find(I b, I e, T val) {
   ^~~~
In file included from /usr/include/c++/6/algorithm:62:0,
                 from foo.cpp:2:
/usr/include/c++/6/bits/stl_algo.h:3784:5: note: candidate: _IIter std::find(_IIter, _IIter, const _Tp&)[with _IIter = __gnu_cxx::__normal_iterator<int*, std::vector<int> >; _Tp = int]
     find(_InputIterator __first, _InputIterator __last,
     ^~~~

In the unit test code, I've linked to above, I've wrapped my functions inside a danstd namespace and I make each call on the form danstd::function(...). Is there any way around having to use fully qualified names to avoid the naming conflicts with std names?

Enlico
  • 23,259
  • 6
  • 48
  • 102
Daniel Näslund
  • 2,300
  • 3
  • 22
  • 27
  • Put your implementation into your own namespace, and explicitly reference it, `yournamespace::find()`. After all, you must've acquired the highly recommend programming practice of explicitly referencing `std::find()`, to invoke the appropriate algorithm, and you never call `find()` out of thin air, relying on the Koenig lookup.. So, this is just being consistent. – Sam Varshavchik Oct 03 '17 at 19:30
  • Related: https://stackoverflow.com/questions/4269034/what-is-the-meaning-of-prepended-double-colon – Rakete1111 Oct 03 '17 at 19:31
  • 1
    This isn't the problem, but `*find(whatever)` will give you problems when the target of the search is not found. In that situation, `find` returns a past-the-end iterator, which is not required to be dereferenceable. – Pete Becker Oct 03 '17 at 20:09
  • @PeteBecker. Yup. Just wrote something quick for demonstrating the compilation error. Code snippet has been edited. – Daniel Näslund Oct 03 '17 at 20:15

4 Answers4

3

I assume that this is due to argument dependent lookup bringing in functions from the std namespace when I pass std::vector iterators as parameters to my functions.

That is correct. Since std::vector::iterator lives in std it will do a lookup in std.

Is there any way around having to use fully qualified names to avoid the naming conflicts with std names?

Unfortunately no. You'll need to qualify you want the one from the global space like

return *::find(begin(v), end(v), 3);
NathanOliver
  • 171,901
  • 28
  • 288
  • 402
  • *"std::vector::iterator lives in std"*. Not necessary BTW, it can simply be `int*`. – Jarod42 Oct 03 '17 at 19:32
  • 1
    @DeiDei It's a great way to use names from a namespace without having to qualify them and without having to use a using statement. – NathanOliver Oct 03 '17 at 19:37
  • @StoryTeller Not really. Don't we say it's best practice to always fully qualify names? Unless there is a very useful corner case, it's just cause for the OP's issue. – DeiDei Oct 03 '17 at 19:38
  • @DeiDei - Well then. Have fun overloading operators. Or calling friend functions defined inline. – StoryTeller - Unslander Monica Oct 03 '17 at 19:40
  • @DeiDei It is also really important when writing generic code. If you have a template function that uses `+` and the type does not provide it as a member function but it is in the same namespace as a free function it will find it and just work. – NathanOliver Oct 03 '17 at 19:40
  • @DeiDei - Also enjoy figuring out what `swap` to call in your own templates, instead of `using std::swap; swap(a,b);` That's just one customization point, as an example. – StoryTeller - Unslander Monica Oct 03 '17 at 19:41
2

You can disambiguate the call as follows

return *::find(begin(v), end(v), 3);

Without that, the compiler is correct, the call is ambiguous as you've noted due to argument dependent lookup

Cory Kramer
  • 114,268
  • 16
  • 167
  • 218
1

C++ is designed to associate functions with types in a given namespace. This is a valid assumption as long as namespaces are used to encapsulate libraries.

The types for your algorithms library are defined in the namespace of another library which already defines the same algorithms. One solution would be to implement your own types… Even one standard container is quite an exercise ;) .

Potatoswatter
  • 134,909
  • 25
  • 265
  • 421
1

I make each call on the form danstd::function(...).

No, you are not. You have a using namespace danstd statement to dump your find() function into the global namespace, and then you are calling it without qualifying it.

So, either qualify it from the global namespace:

namespace danstd {
    ...
    template <typename I, typename T>
    I find(I b, I e, const T &val) { ... }
}

using namespace danstd;

auto i = ::find(begin(v), end(v), 3);

Or else drop the using statement and fully qualify the function call using your namespace:

namespace danstd {
    ...
    template <typename I, typename T>
    I find(I b, I e, const T &val) { ... }
}

// using namespace danstd;

auto i = danstd::find(begin(v), end(v), 3);
Remy Lebeau
  • 555,201
  • 31
  • 458
  • 770