假设我有一个自定义类型(我可以扩展):
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>() const
-
operator tuple<const int&,const string&>() const
它们导致赋值错误,告诉“operator =
没有为 tuple<int&,string&> = Foo
重载”。我猜这是因为“转换为任何类型 X + 为 operator="推导模板参数 X 不能一起工作,一次只能其中一个。
不完美的尝试:
因此我尝试为领带的确切类型实现转换运算符:
分配现在有效,因为类型现在(转换后)完全相同,但这不适用于我想支持的三个场景:
- 如果 tie 绑定(bind)了不同但可转换类型的变量(即在客户端将
int a;
更改为long long a;
),则由于类型必须完全匹配,所以它会失败。这与将元组分配给允许可转换类型的引用元组的通常用法相矛盾。(1) - 转换运算符需要返回一个必须给定左值引用的关系。这不适用于临时值或 const 成员。(2)
- 如果转换运算符不是 const,则
const Foo
的赋值也会失败在右手侧。要实现转换的 const 版本,我们需要去掉 const 主体成员的 const 特性。这很丑陋,可能会被滥用,从而导致未定义的行为。
我只看到提供自己的tie
的替代方法函数 + 类以及我的“可绑定(bind)”对象,这让我不得不复制 std::tie
的功能我不喜欢(不是我觉得这样做很困难,而是不得不这样做感觉不对)。
我认为归根结底,这是仅库元组实现的一个缺点。它们并不像我们希望的那样神奇。
编辑:
事实证明,似乎没有解决上述所有问题的真正解决方案。一个很好的答案可以解释为什么这无法解决。特别是,我希望有人能解释一下为什么“失败的尝试”不可能奏效。
<支持> (1):一个可怕的 hack 是将转换编写为模板并在转换运算符中转换为请求的成员类型。这是一个可怕的黑客,因为我不知道在哪里存储这些转换的成员。在 this demo我使用静态变量,但这不是线程可重入的。
<支持> (2): 可以应用与 (1) 相同的技巧。
最佳答案
为什么当前尝试失败
std::tie(a, b)
产生 std::tuple<int&, string&>
.
此类型与 std::tuple<int, string>
无关等等
std::tuple<T...>
s 有几个赋值运算符:
- 一个默认的赋值运算符,它接受
std::tuple<T...>
- 带有类型参数包的元组转换赋值运算符模板
U...
, 这需要std::tuple<U...>
- 具有两个类型参数的对转换赋值运算符模板
U1, U2
, 这需要std::pair<U1, U2>
对于这三个版本,存在复制和移动变体;添加 const&
或 &&
到他们采取的类型。
赋值运算符模板必须从函数参数类型(即赋值表达式的 RHS 类型)推导出它们的模板参数。
Foo
中没有转换运算符, 这些赋值运算符都不适用于 std::tie(a,b) = foo
.
如果将转换运算符添加到 Foo
,
那么只有默认的赋值运算符变得可行:
模板类型推导不考虑用户定义的转换。
也就是说,您不能从类型 Foo
中推断出赋值运算符模板的模板参数。 .
由于在隐式转换序列中只允许进行一次用户定义的转换,因此转换运算符转换为的类型必须与默认赋值运算符的类型完全匹配。也就是说,它必须使用与 std::tie
的结果完全相同的元组元素类型。 .
为了支持元素类型的转换(例如,将 Foo::a
赋值给 long
),Foo
的转换运算符必须是模板:
struct Foo {
int a;
string b;
template<typename T, typename U>
operator std::tuple<T, U>();
};
但是,std::tie
的元素类型是引用。
由于您不应该返回对临时的引用,
运算符模板内的转换选项非常有限
(堆、类型双关语、静态、线程本地等)。
关于c++ - 制作自定义类型 "tie-able"(与 std::tie 兼容),我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/26276943/