2013-03-17 3 views
4

Это действительно о плохом представлении об обмене двумя ссылками. Ссылки не должны быть сброшены, поэтому они не должны быть возможными. Это я знаю.Способ обмена двумя ссылками в C++

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

int a = 0, b = 1; 
int *pA = &a, *pB = &b; 
std::swap(pA, pB); 

сейчас * УМ 1 и * рв 0, но по-прежнему 0 и Ь по-прежнему 1. Это, однако, невозможно при ссылках:

int a = 0, b = 1; 
int &rA = a, &rB = b; 
std::swap(pA, pB); 

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

template <class _Ty> 
struct resetable_ref { 
    _Ty &ref; 

    inline resetable_ref(resetable_ref &r) 
     :ref(r.ref) 
    {} 

    inline resetable_ref(_Ty &_ref) 
     :ref(_ref) 
    {} 

    inline resetable_ref &operator =(resetable_ref &r) 
    { 
     if(sizeof(resetable_ref) == sizeof(void*)) // compile-time constant (true) 
      *reinterpret_cast<void**>(this) = *reinterpret_cast<void**>(&r); 
     else 
      memcpy(this, &r, sizeof(resetable_ref)); // optimized away as dead code 
     return *this; 
    } 

    inline operator _Ty &() 
    { 
     return ref; 
    } 
}; 

int a = 0, b = 1; 
resetable_ref<int> rrA(a), rrB(b); 
std::swap(rrA, rrB); 

Теперь еще 0 и б еще 1, и ссылки внутри пА и РРК меняются местами. Жаль, что он не работает без довольно уродливого оператора =(). По крайней мере, это работает для меня в MSVC, не уверен, что g ++ примет его (но я думаю, что он должен).

Весь ссылочный своп предполагается использовать в объекте, который построен со ссылкой на другой объект внутри, и я хотел бы сделать функцию swap() на них. Я бы хотел избежать использования указателей из-за хорошей не-nullarity функции ссылок. Это также было бы более приятным дизайном (за исключением самого resetable_ref).

Есть ли у кого-нибудь лучшее представление о том, как это сделать? Может ли кто-нибудь подумать о некоторых проблемах с совместимостью/неопределенным поведением, с которыми можно столкнуться?

Я написал большую часть кода без компиляции, пожалуйста, несите меня, если вы заметили какую-либо опечатку.

EDIT: Мне кажется, что у многих людей отсутствует точка вопроса. Я знаю, как использовать указатели или даже как конвертировать указатели в хороший шаблон. Вопрос отмечен «взломать», и это то, что ожидается. Не говорите мне, как «не делайте этого, используйте указатель», поскольку это не то, что я спросил. Если вам не нравится эта тема, не отвечайте, но не уменьшайте вопрос только потому, что будете использовать указатель.

+1

Я полагаю 'станд :: reference_wrapper' может работать. Тем не менее, он никогда не использовал его. – chris

+4

Почему бы вам не использовать указатель, а не ссылку внутри вашего класса, сделать его конфиденциальным, а затем предоставить подходящие функции-члены, которые будут выступать в качестве аксессуаров к базовому 'int'? Таким образом, вы можете избежать всех неприятных проблем с reinterpret_cast/потенциально-неопределенным поведением. –

+0

@chris спасибо, я понятия не имел, что существует –

ответ

3

Переменчивая ссылка ... no more tha указатель, для которого вам нужны неявные разыменование, такие как ссылка.

template<class T> 
class mutable_ref 
{ 
public: 
    mutable_ref(T& t) :p(&t) 
    {} 

    operator T&() { return *p; } 
    operator const T&() const { return *p; } 

    void swap(mutable_ref& s) 
    { std::swap(p,s.p); } 

private: 
    T* p; 
}; 

// just in case you also want to specialize std::swap for mutable_ref. 
// not necessary, since the generic std::swap<T> use twice =, that is available. 
namespace std 
{ 
    template<class T> 
    void swap(mutable_ref<T>& a, mutable_ref<T>& b) 
    { a.swap(b); } 
} 

Обратите внимание на отсутствие CTOR по умолчанию, с инициализации с т е р ссылку, что делает этот класс не-обнуляемым.

Единственная проблема заключается в том, что для доступа к возможным членам T, являющимся "." оператор, не перегружаемый, для этого вам нужно что-то elese.

Простая вещь использование * и ->, как ...

T* operator->() const { return p; } 
T& operator*() const { return *p; } 

быть определен внутри mutable_ref Декларация-

+0

О да, я знаю C++, я знаю, что это можно сделать. Вопрос был конкретно в том, как обмениваться ссылками. Спасибо за ваш ответ, это хороший код. –

+0

Проблема в том, что ... это невозможно. Вот почему я опубликовал «несовершенное обходное решение», которое просто определяет «mutable_reference», не присутствующий на языке C++. –

+1

Я думаю, что он присутствует, хотя: stdlib является частью «основного языка», а stdlib содержит 'std :: reference_wrapper'. Как вы подразумевали, из-за отсутствия 'operator.', прямо сейчас пользователь должен написать' refwrap.get(). Member' - но, надеюсь, Bjarne и co будут получать 'operator.' в язык в следующий раз;) –

Смежные вопросы