3

Here's my case:

I am trying to use a library that has a type Foo::a, and specifies a Foo::swap as well. Another library that I am consuming has a std::vector<Foo::a> instantiation. I am trying to compile this on Windows using Visual Studio 11.0 and notice that the std::vector::swap maps down to _Swap_adl which does an unqualified swap call.

This is what gets me into issues with ADL and ambiguous function resolutions. Is there some magic that will allow me to use Foo::swap (heck even std::swap :)), without making some "major" change to the libraries that I am consuming (stuff that is short of removing/renaming swap from Foo, etc)?

Edit: Adding a minimal example that captures what is going on and the error.

#include <iostream>
#include <vector>

namespace Foo
{
    class MyType
    {
    public:     
        float dummy;
    };

    template <class T>
    void swap(T& a, T& b)
    {
        T c(a);
        a = b;
        b = c;
    }
}

using namespace Foo;

class MyClass
{
public:
    std::vector<MyType> myStructArr;
};

std::vector<MyType> getMyTypeArray()
{
    MyType myType;
    std::vector<MyType> myTypeArray;
    myTypeArray.push_back(myType);
    return myTypeArray;
}

namespace std
{
    template <>
    void swap<MyType*>(MyType*& a, MyType*& b)
    {
        MyType* c(a);
        a = b;
        b = c;
    }

    template <>
    void swap<MyType>(MyType& a, MyType& b)
    {
        MyType c(a);
        a = b;
        b = c;
    }
}

int main(int argc, char* argv[])
{
    MyClass m;

    MyType myTypeLocal;
    std::vector<MyType> myTypeArrayLocal;
    myTypeArrayLocal.push_back(myTypeLocal);

    //m.myStructArr = myTypeArrayLocal;
    m.myStructArr = getMyTypeArray();

    return 0;
}

I won't comment on the efficiency of the code, as its something that I just have to work with, but the error log at @ http://pastebin.com/Ztea46aC gives a fair idea of what's going on internally. Is this a compiler specific issue, or is there a deeper learning to be gained from this piece of code?

Edit 2: I've tried specializing for the particular type in question, but that doesn't resolve the ambiguity. Any pointers on why this is so would be helpful as well.

namespace std
{
    template <>
    void swap<MyType*>(MyType*& a, MyType*& b)
    {
        MyType* c(a);
        a = b;
        b = c;
    }

    template <>
    void swap<MyType>(MyType& a, MyType& b)
    {
        MyType c(a);
        a = b;
        b = c;
    }
}

http://pastebin.com/sMGDZQBZ is the error log from this attempt.

user2184879
  • 183
  • 6
  • 1
    Show some code. What you've described is how it's supposed to be done. – Pete Becker Mar 22 '13 at 21:50
  • 2
    What is `Foo::swap`? Is it a member function named `swap` in a class named `Foo`? Or is it a free function named `swap` in a namespace named `Foo`? And are you saying the unqualified call to `swap` is resolving to `std::swap`? – Praetorian Mar 22 '13 at 21:51
  • @user2184879 Ok, so what's the problem? From the description it sounds like everything should work right. As Pete said earlier, post code showing at least the definitions (and namespaces) of all types involved. – Praetorian Mar 22 '13 at 21:54
  • What's wrong with adding `namespace std { using Foo::swap; }` in your code? (I'm not sure if I'm interpreting this question correctly) – Tom Knapen Mar 22 '13 at 21:57
  • Apologies am on a device with a small form factor for some while...the problem is that std has a swap as well, leading to a conflict for which swap to use, as the Foo::swap is brought into consideration by ADL...@Tom is such hiding of the function a hack and are there some issues I need to be aware of? I think u have the question down just fine...@All will post code as soon as I get near my 15" – user2184879 Mar 22 '13 at 22:08
  • 1
    post a complete small example that exhibits the problem – Cheers and hth. - Alf Mar 22 '13 at 22:09
  • 1
    @TomKnapen messing with std is undefined behavior. – daniel gratzer Mar 23 '13 at 03:03
  • 2
    @TomKnapen - well, yes, undefined behavior to try to add a template that duplicates the `swap` template that's already in `std`. But it's okay to add template specializations that depend on a user-defined type. So adding `void swap(MyType&, MyType&)` to `std` would be okay, although that's not what this code does. – Pete Becker Mar 23 '13 at 11:29

2 Answers2

0

As the error message says, the function call is ambiguous. There are two versions of swap that could be applied: the one in namespace Foo and the one in namespace std. The first is found by argument-dependent lookup. The second is found because vector is defined in std, so sees std::swap (as designed).

This isn't a situation where one declaration hides another; it is simply a set of possible overloads, so the usual ordering rules for selecting an overloaded function apply. The two versions of swap take the same argument types, so there is no preference for one over the other.

Pete Becker
  • 74,985
  • 8
  • 76
  • 165
  • Yes. So there isn't any option other than change the name of the function/change the semantics of the getMyTypeArray? Ideally, some way As mentioned, the second swap is in a library, and I'd like something that doesn't alter code significantly. I've tried specializing for the particular case, so that changes remain in client code (see Edit 2), but to no avail. – user2184879 Mar 23 '13 at 18:33
  • @user2184879 - that's not right. The vector holds objects of type `MyType`, so it will be swapping `MyType` objects, not `MyType*` objects. You need a specialization for `swap(Mytype&, MyType&)`. – Pete Becker Mar 23 '13 at 18:46
  • @Pete...do see the error log after Edit 2 for the specialization that gives the ambiguity. The std::vector move copy code internally relies on swapping MyType*&'s is my guess, and thus my attempt at the MyType*& specialization. FWIW, I've tried specializing with MyType& as well. – user2184879 Mar 23 '13 at 19:00
  • @user2184879 - the first error in that set of messages is at line 40 in main.cpp; in your posted code, line 40 is blank. So obviously the error message came from some **other** code. It's very hard diagnose code that you don't post. – Pete Becker Mar 23 '13 at 19:54
  • My bad...I'd tinkered with the code a bit, changing the assignment to a swap, that led to MyType* requiring specialization. I've reverted back to assignment, and specialized MyType* and MyType, though, for assignment, MyType is what that needs to be specialized for, as Pete mentioned. The error remains though. Updating the code segment with the latest code, and the matching error log in edit2. – user2184879 Mar 23 '13 at 20:18
0

Pete's answer explains the situation. Both templates for swap are equally favoured during the overload resolution steps, so the call is ambiguous - in any context where both namespaces are visible. Specialisation is the correct approach to this problem, however based on your errors you've forgotten to remove the offending template Foo::swap - see here for what your code should look like.

Here's what everything looks like - I just replaced the std namespace with the bar namespace.

#include <iostream>

namespace bar { //std namespace

template<typename T>
void swap(T s, T t){ std::cout << "bar swap\n"; }

template<typename S>
struct vec {
    S s;
    void error() { swap(s, s); }
};}

namespace foo { //your namespace

struct type {};

/*this template is not allowed/not a good idea because of exactly your problem
template<typename T>
void swap(T s, T t){ std::cout << "foo swap\n"; }
*/

//you will have to rename foo::swap to something like this, and make it call
//specialise std::swap for any types used in std containers, so that they called yuor
//renamed foo::swap
template<typename T>
void swap_proxy(T s, T t){ std::cout << "foo swap (via proxy) \n"; }

}

namespace bar {
template<> void swap(foo::type s, foo::type t) { std::cout << "foo swap\n"; }
}

int main()
{
    //instead specialise std::swap with your type

    bar::vec<foo::type> myVec;
    myVec.error();

    std::cout << "Test\n";
    operator<<(std::cout, "Test\n");
}

All that said I'll try and cook up a templated alternative - however it will call std::swap in std code (it will make foo::swap a worse alternative, so there will be no ambiguity.


This avoids changing the name of swap, but still changes its definition slightly - although you shouldn't have to tamper with code inside swap, only code that calls will have to cast as explained

#include <iostream>

namespace bar { //std namespace

template<typename T>
void swap(T s, T t){ std::cout << "bar swap\n"; }

template<typename S>
struct vec {
    S s;
    void error() { swap(s, s); }
};}

namespace foo { //your namespace

// Include this pattern (The infamous Curiously Recurring Template Pattern) in foo namespace

template<template<class> class Derived, typename t>
struct Base : public t { Base(){} Base(Derived<t> d){} };

template<typename t> struct Derived : public Base<Derived, t> {};

template<class t> using UnWrapped = Base<Derived, t>;
template<class t> using Wrapped = Derived<t>;


//we redefine swap to accept only unwrapped t's - meanwhile
//we use wrapped t's in std::containers
template<typename T>
void swap(UnWrapped<T> s, UnWrapped<T> t){ std::cout << "foo swap\n"; }

struct type {
};

}

int main()
{
    //use the wrapped type
    typedef foo::Wrapped<foo::type> WrappedType;
    typedef foo::UnWrapped<foo::type> UnWrappedType;

    bar::vec<WrappedType> myVec;
    //this is the function which makes the ambiguous call
    myVec.error();
    //but instead calls bar::swap

    //but -- it calls foo swap outside of bar! - any you didn't
    //have to change the name of swap, only alter definition slightly
    swap(WrappedType(), WrappedType());
    //safe outside of bar
    swap(UnWrappedType(), UnWrappedType());
    //the following no longer works :/
    //swap<foo::type>(foo::type(), foo::type());
    //instead, yuo will have to cast to UnWrapped<type>
}
Community
  • 1
  • 1
user3125280
  • 2,779
  • 1
  • 14
  • 23