std:: своп странность с G++

Это странность, когда я не знаю, если это со стандартом C++, с моим компилятором (G++ версия 4.6.3 на Ubuntu 12.04, которая является последней версией долгосрочной поддержки Ubuntu) или со мной, кто не понимает;-)

Код, о котором идет речь, так же прост:

#include <algorithm>    // for std::swap
void f(void)
{
    class MyClass { };
    MyClass aa, bb;
    std::swap(aa, bb);          // doesn't compile
}

При попытке компиляции с помощью G ++ компилятор выдает следующее сообщение об ошибке:

test.cpp: In function ‘void f()’:
test.cpp:6:21: error: no matching function for call to ‘swap(f()::MyClass&, f()::MyClass&)’
test.cpp:6:21: note: candidates are:
/usr/include/c++/4.6/bits/move.h:122:5: note: template<class _Tp> void std::swap(_Tp&, _Tp&)
/usr/include/c++/4.6/bits/move.h:136:5: note: template<class _Tp, long unsigned int _Nm> void std::swap(_Tp (&)[_Nm], _Tp (&)[_Nm])

Удивительным результатом является то, что простое удаление определения класса из функции делает этот код хорошо компилируемым:

#include <algorithm>    // for std::swap
class MyClass { };
void f(void)
{
    MyClass aa, bb;
    std::swap(aa, bb);          // compiles fine!
}

Так что же, std::swap() не должен работать с классами, которые являются частными для функций? Или это ошибка в G++, может быть, конкретная версия G++, которую я использую?

Еще более озадачивает то, что следующее снова работает, несмотря на то, что MyListClass также является закрытым (но расширяет "официальный" класс, для которого, возможно, существует конкретная реализация swap ()):

#include <algorithm>        // for std::swap
#include <list>             // for std::list
void g(void)
{
    class MyListClass : public std::list<int> { };
    MyListClass aa, bb;
    std::swap(aa, bb);              // compiles fine!
}

Но просто перейдите от объектов к указателям, и компиляция снова не удастся:

#include <algorithm>        // for std::swap
#include <list>             // for std::list
void g(void)
{
    class MyListClass : public std::list<int> { };
    MyListClass aa, bb;
    MyListClass* aap = &aa;
    MyListClass* bbp = &bb;
    std::swap(aap, bbp);    // doesn't compile!
}

Конечно, в моем реальном приложении классы более сложные; Я максимально упростил код, чтобы все еще воспроизвести проблему.

1 ответ

Решение

Если вы работаете в режиме C++03, что, как мне кажется, имеет место, вам не разрешается использовать локально определенный тип в шаблоне. Если это так, вы можете определить свои типы на уровне пространства имен, чтобы он работал, или же вы можете скомпилировать в режиме C++11, где он должен компилироваться.[*]

Если вам интересно, почему работает второй случай, стандарт не предусматривает специализации

template <typename T> void swap(T&,T&) // [1] 

как std::list сам шаблон, и вы не можете частично специализировать функции шаблона. Он предоставляет другой базовый шаблон:

template <typename T, typename A> void swap(list<T,A>&,list<T,A>&); // [2]

Теперь, как и в предыдущем случае, компилятор не может использовать ваш локальный тип с [1], поэтому он отбрасывается. Затем он пытается [2] и находит, что может преобразовать lvalue локального типа в ссылки на базу std::list<int>и после этого преобразования [2] является хорошим кандидатом. Затем он позвонит

std::swap(static_cast<std::list<int&>>(aa),static_cast<std::list<int&>>(bb));

который не использует локальный тип, а скорее уровень пространства имен std::list<int>,

С другой стороны, тот факт, что он компилируется, не означает, что он делает то, что вы хотите. В частности, если расширенный тип MyListClass добавляет любые новые переменные-члены, они не будут заменены.

Все это, как говорится, и просто замечание: вы не должны наследовать от стандартных контейнеров, так как они никогда не были предназначены для наследования от.

[*] Отказ от ответственности: я не знаю, поддерживается ли эта функция в этой конкретной версии компилятора, вам придется перепроверить.

Другие вопросы по тегам