Вот мой случай:
Я пытаюсь использовать библиотеку, которая имеет тип Foo::a
и указывает Foo::swap
также. Другая библиотека, которую я потребляю, имеет std::vector<Foo::a>
конкретизации. Я пытаюсь скомпилировать это в Windows с помощью Visual Studio 11.0 и замечаю, что 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 это журнал ошибок от этой попытки.
Как говорится в сообщении об ошибке, вызов функции неоднозначен. Есть две версии swap
что может быть применено: один в пространстве имен Foo
и тот, в пространстве имен std
, Первый находится в зависимости от аргумента поиска. Второй найден потому что vector
определяется в std
так видит std::swap
(как задумано).
Это не ситуация, когда одно объявление скрывает другое; это просто набор возможных перегрузок, поэтому применяются обычные правила упорядочения для выбора перегруженной функции. Две версии swap
принимать те же типы аргументов, чтобы не было предпочтения одного над другим.
Ответ Пита объясняет ситуацию. Оба шаблона для подкачки одинаково предпочтительны на этапах разрешения перегрузки, поэтому вызов неоднозначен — в любой контекст, в котором видны оба пространства имен. Специализация — правильный подход к этой проблеме, однако, основываясь на своих ошибках, вы забыли удалить шаблон, вызвавший оскорбления. Foo::swap
— увидеть Вот как должен выглядеть ваш код.
Вот как все выглядит — я просто заменил 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
в коде std (это сделает foo::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>
}