【发布时间】:2014-10-09 11:13:24
【问题描述】:
假设我有一个自定义类型(我可以扩展):
struct Foo {
int a;
string b;
};
如何使该对象的实例可分配给 std::tie,即引用的 std::tuple?
Foo foo = ...;
int a;
string b;
std::tie(a, b) = foo;
尝试失败:
不可能重载tuple<int&,string&> = Foo 的赋值运算符,因为赋值运算符是必须是左侧对象成员的二元运算符之一。
所以我尝试通过实现一个合适的元组转换运算符来解决这个问题。以下版本失败:
operator tuple<int,string>() constoperator tuple<const int&,const string&>() const
它们导致分配错误,告诉“operator = 没有为tuple<int&,string&> = Foo 重载”。我猜这是因为“转换为任何类型 X + 为 operator=" 推导模板参数 X 不能一起工作,一次只能其中一个。
不完美的尝试:
因此我尝试为领带的确切类型实现转换运算符:
-
operator tuple<int&,string&>() constDemo -
operator tuple<int&,string&>()Demo
分配现在有效,因为类型现在(转换后)完全相同,但这不适用于我想支持的三个场景:
- 如果 tie 绑定了不同但可转换类型的变量(即在客户端将
int a;更改为long long a;),则由于类型必须完全匹配,它会失败。这与将元组分配给允许可转换类型的引用元组的通常用法相矛盾。(1) - 转换运算符需要返回一个必须给定左值引用的关系。这不适用于临时值或 const 成员。(2)
- 如果转换运算符不是 const,则右侧的
const Foo的赋值也会失败。要实现转换的 const 版本,我们需要去掉 const 主体成员的 const 特性。这很丑陋,可能会被滥用,从而导致未定义的行为。
我只看到提供我自己的 tie 函数 + 类以及我的“可绑定”对象的替代方案,这使我不得不复制我不喜欢的 std::tie 的功能(不是我觉得这样做很困难,但不得不这样做感觉不对)。
我认为归根结底,这是仅库元组实现的一个缺点。它们并不像我们希望的那样神奇。
编辑:
事实证明,似乎没有解决上述所有问题的真正解决方案。一个很好的答案可以解释为什么这无法解决。特别是,我希望有人能解释一下为什么“失败的尝试”不可能奏效。
(1):一个可怕的 hack 是将转换编写为模板并在转换运算符中转换为请求的成员类型。这是一个可怕的黑客,因为我不知道在哪里存储这些转换的成员。在this demo 中,我使用静态变量,但这不是线程可重入的。
(2): 可以应用与 (1) 相同的技巧。
【问题讨论】:
-
我对此感到非常震惊,但这个优秀问题似乎没有预先存在的副本。
-
@MagnusHoff:
operator=必须是成员 ([C++11: 13.5.3/1]),您不能更改tuple<int, string>的定义。 -
我认为基本问题如下:
std::tie产生一个引用元组,即tuple<int&, string&>。无论您将Foo转换为什么,它都不能包含 const 转换运算符的非常量引用。因此转换为的类型与tie类型不匹配。因此,默认的赋值运算符是不可行的,必须使用赋值运算符模板。但是这个模板的推论失败了,因为Foo本身不是tuple。 -
“失败的尝试”不起作用,因为模板参数推导(发生在重载决议之前)不考虑用户定义的隐式转换(在重载决议期间考虑)。
-
@leemes 有两个非模板
operator=s 接受完全相同的类型。