Обновление:Смотрите полный ответ ниже. Короткий ответ — нет, не напрямую. Вы можете создать косвенную ссылку, используя std::reference_wrapper
, или достичь того же эффекта в более общем смысле с помощью указателей (, но без синтаксического сахара и дополнительной безопасности ссылок ).
Я спрашиваю, потому что кортежи представляют собой удобную единицу хранения с переменным числом аргументов в C++11. Теоретически кажется разумным, чтобы один элемент кортежа содержал ссылку на другой элемент того же кортежа. (Замените «ссылку» на «указатель», и это сработает на практике. )Дьявол кроется в деталях построения такого кортежа. Рассмотрим следующий пример:
#include <tuple>
#include <iostream>
class A
{
public:
A() : val(42) { }
int val;
};
class B
{
public:
B(A &a) : _a(a) { }
int val() { return _a.val; }
private:
A &_a;
};
int main()
{
A a;
B b(a);
std::tuple<A, B> t1(a, b);
a.val = 24;
std::cout << std::get<0>(t1).val << "\n"; // 42
std::cout << std::get<1>(t1).val() << "\n"; // 24
return 0;
}
Второй элемент кортежа t1
ссылается на автоматическую переменную a
вместо первого элемента в t1
.Есть ли способ построить кортеж так, чтобы один элемент кортежа мог содержать ссылку на другой элемент в том же кортеже? Я знаю, что вы могли бы добиться такого результата, создав кортеж ссылок, например:
int main()
{
A a;
B b(a);
std::tuple<A &, B &> t2 = std::tie(a, b);
a.val = 24;
std::cout << std::get<0>(t2).val << "\n"; // 24
std::cout << std::get<1>(t2).val() << "\n"; // 24
return 0;
}
Но для моих целей это мошенничество, поскольку второй элемент в t2
по-прежнему в конечном счете ссылается на объект, который находится за пределами кортежа. Единственный способ, который я могу придумать, это компилировать нормально, но может содержать неопределенное поведение [отредактировано, чтобы отразить более краткий пример, предоставленный Говардом Хиннантом]:
int main()
{
std::tuple<A, B> t3( A(), B(std::get<0>(t3)) ); // undefined behavior?
std::get<0>(t3).val = 24;
std::cout << std::get<0>(t3).val << "\n";
std::cout << std::get<1>(t3).val() << "\n"; // nasal demons?
}
Редактировать :Вот минимальная тестовая программа, которая возвращает не -нулевой статус выхода при компиляции с использованием g++ 4.7 с -O2 или выше. Это предполагает либо неопределенное поведение, либо ошибку в gcc.
#include <tuple>
class Level1
{
public:
Level1() : _touched(false), _val(0) { }
void touch()
{
_touched = true;
}
double feel()
{
if ( _touched )
{
_touched = false;
_val = 42;
}
return _val;
}
private:
bool _touched;
double _val;
};
class Level2
{
public:
Level2(Level1 &level1) : _level1(level1) { }
double feel()
{
return _level1.feel();
}
private:
int _spaceholder1;
double _spaceholder2;
Level1 &_level1;
};
class Level3
{
public:
Level3(Level2 &level2) : _level2(level2) { }
double feel()
{
return _level2.feel();
}
private:
Level2 &_level2;
};
int main()
{
std::tuple<Level3, Level2, Level1> levels(
Level3(std::get<1>(levels)),
Level2(std::get<2>(levels)),
Level1()
);
std::get<2>(levels).touch();
return ! ( std::get<0>(levels).feel() > 0 );
}