绕过参数依赖查找歧义的非突兀方式

时间:2013-03-22 21:35:21

标签: c++ argument-dependent-lookup ambiguous-call

这是我的情况:

我正在尝试使用类型为Foo::a的库,并指定Foo::swap。我正在使用的另一个库具有std::vector<Foo::a>实例化。我正在尝试使用Visual Studio 11.0在Windows上编译它,并注意std::vector::swap映射到_Swap_adl,它执行无限制的交换调用。

这就是让我了解ADL和模糊功能解决方案的问题。是否有一些魔法允许我使用Foo::swap(甚至std::swap :)),而不对我正在使用的库进行一些“重大”更改(缺少删除/重命名的内容)从Foo等交换?)

编辑: 添加一个捕获正在发生的事情和错误的最小示例。

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

我不会评论代码的效率,因为它只是我必须使用的东西,但@ http://pastebin.com/Ztea46aC 的错误日志可以很好地了解代码的含义。在内部进行。这是一个特定于编译器的问题,还是可以从这段代码中获得更深入的学习?

编辑2: 我尝试过专门针对特定类型的问题,但这并不能解决这种模糊性问题。关于为什么会出现这种情况的任何指示都会有所帮助。

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 是此尝试的错误日志。

2 个答案:

答案 0 :(得分:0)

如错误消息所示,函数调用不明确。可以应用swap的两个版本:名称空间Foo中的一个和名称空间std中的一个版本。第一个是通过参数依赖查找找到的。找到第二个是因为vector中定义了std,所以看std::swap(按照设计)。

这不是一个声明隐藏另一个声明的情况;它只是一组可能的重载,因此适用于选择重载函数的通常排序规则。 swap的两个版本采用相同的参数类型,因此没有一个优先于另一个。

答案 1 :(得分:0)

皮特的回答解释了这种情况。在重载解析步骤中,交换的两个模板都同样受欢迎,因此调用是不明确的 - 在任何上下文中,两个命名空间都是可见的。专业化是解决此问题的正确方法,但根据您的错误,您忘记删除有问题的模板Foo::swap - 请参阅here了解您的代码应该是什么样的。

以下是所有内容 - 我只是将std命名空间替换为bar命名空间。

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

所有这一切都说我会尝试制作一个模板化的替代方案 - 但它会在标准代码中调用std::swap(它会使foo::swap成为更糟糕的替代方案,因此不存在歧义。< / p>


这可以避免更改交换名称,但仍然会略微更改其定义 - 尽管如此 你不应该篡改swap中的代码,只有那些调用的代码必须按照解释

进行强制转换
#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>
}