元组的一个元素可以引用另一个元素吗
Can one element of a tuple reference another?
更新:请参阅下面的完整答案。简短的回答是否定的,不是直接的。您可以使用std::reference_wrapper
创建间接引用,也可以使用指针实现同样的效果(但不需要语法糖和添加引用的安全性)。
我之所以这么问,是因为元组在C++11中是一个方便的变量存储单元。理论上,元组中的一个元素包含对同一元组中另一个元素的引用听起来是合理的。(将"reference"替换为"pointer",它在实践中是有效的。)构建这样一个元组的细节才是关键。考虑以下示例:
#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
中的第二个元素最终仍然引用了一个位于元组之外的对象。我能想到的唯一方法是编译良好,但可能包含未定义的行为[编辑以反映Howard Hinnant提供的更简洁的示例]:
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 );
}
这对我有效:
#include <tuple>
#include <iostream>
int main()
{
std::tuple<int&, int> t(std::get<1>(t), 2);
std::cout << std::get<0>(t) << 'n';
std::get<1>(t) = 3;
std::cout << std::get<0>(t) << 'n';
}
更新
我刚刚在CWG邮件列表上询问了这个案例。Mike Miller向我保证,根据3.8p6项目符号2:,这个是未定义的行为
程序有未定义的行为,如果:
- glvalue用于访问非静态数据成员或调用对象的非静态成员函数,或者
如果tuple
是一个聚合,这将是一个定义良好的行为,但由于tuple
有一个用户声明的构造函数,因此适用3.8p6b2。
然而,这是有效的,并避免了UB:
#include <tuple>
#include <functional>
#include <cassert>
int main()
{
int dummy;
std::tuple<std::reference_wrapper<int>, int> t(dummy, 2);
std::get<0>(t) = std::get<1>(t);
assert(std::get<0>(t) == 2);
std::get<1>(t) = 3;
assert(std::get<0>(t) == 3);
}
据我所知,对尚不存在的对象进行别名是一种定义良好的行为。这与您在构造函数初始化列表中使用this
的情况相同,这很不幸,但很常见,而且定义良好。
- 使用std::transform将一个范围的元素添加到另一个范围中
- 检查 2D 网格的某个元素是否与另一个元素共享对角线、水平线或垂直线
- 为什么在 std::map 上移动无法将元素从一个映射移动到另一个映射
- 如何从一个容器中获取某些元素并将其转换插入到另一个容器中?
- 如何从另一个向量中的另一个第一元素减去向量中的第一元素
- 保留从一个多集复制到另一个多集的元素的顺序
- 如何从一个向量中删除最小元素并添加到另一个向量,而第一个变为空?
- C++:检查向量中的元素是否大于另一个具有相同索引的元素的有效方法?
- 从另一个向量中搜索和找到元素的有效方法
- 如果在C 中已经回荡了另一个相同值的阵列元素,请防止循环回荡
- 当我尝试将一个向量元素的值分配给另一个向量元素时,为什么我的应用程序会崩溃
- 如何将元素添加到从另一个线程绑定到 XAML 的 IVector
- 使用对另一个元组不同类型的元素的非常量引用来初始化元组的元素
- 并行算法将向量的元素分配到另一个元素的元素
- C 初始化矢量每个元素,带有另一个向量的参数
- 模板函数,它使用 n_copy 复制前 n 个元素形成一个向量,另一个向量导致编译错误
- 递增迭代器以指向另一个迭代器的下一个元素
- 如何转换一个类方法以修改另一个类的私有元素?
- 将映射从第二个元素复制到另一个地图
- 使用擦除-删除范例将元素从一个向量移动到另一个向量