C++ “制作自定义类型”;“有能力”;(与std::tie兼容)
假设我有一个自定义类型(可以扩展): 如何使此对象的实例可分配给引用的C++ “制作自定义类型”;“有能力”;(与std::tie兼容),c++,c++11,tuples,std,C++,C++11,Tuples,Std,假设我有一个自定义类型(可以扩展): 如何使此对象的实例可分配给引用的std::tie,即std::tuple Foo foo = ...; int a; string b; std::tie(a, b) = foo; 尝试失败: 无法重载tuple=Foo的赋值运算符,因为赋值运算符是必须是左侧对象成员的二进制运算符之一 所以我试图通过实现一个合适的元组转换操作符来解决这个问题。以下版本失败: 运算符tuple()const 运算符tuple()const 它们在赋值时导致错误,说明
std::tie
,即std::tuple
Foo foo = ...;
int a;
string b;
std::tie(a, b) = foo;
尝试失败:
无法重载tuple=Foo的赋值运算符,因为赋值运算符是必须是左侧对象成员的二进制运算符之一
所以我试图通过实现一个合适的元组转换操作符来解决这个问题。以下版本失败:
运算符tuple()const
运算符tuple()const
operator=
没有重载tuple=Foo
”。我猜这是因为“转换为任何类型X+推导operator=”的模板参数X不一起工作,一次只能转换其中一个
不完美的尝试:
因此,我尝试为领带的确切类型实现一个转换运算符:
运算符tuple()const
运算符元组()
int a;
更改为long-long a;
),它将失败,因为类型必须完全匹配。这与通常将元组分配给允许可转换类型的引用元组的用法相矛盾const Foo
,赋值也会失败。要实现const版本的转换,我们需要去掉const主题成员的const。这是丑陋的,可能被滥用,导致未定义的行为tie
function+类和我的“可绑定”对象,这使我不得不复制std::tie
的功能,我不喜欢它(并不是说我觉得很难做到,而是觉得必须这样做是不对的)
我认为,最终的结论是,这是纯库元组实现的一个缺点。它们不像我们希望的那么神奇
编辑:
事实证明,似乎没有解决上述所有问题的真正解决方案。一个很好的答案可以解释为什么这是无法解决的。特别是,我想有人解释一下为什么“失败的尝试”不可能奏效
(1) :一个可怕的技巧是将转换写入模板,并在转换运算符中转换为请求的成员类型。这是一个可怕的黑客行为,因为我不知道在哪里存储这些已转换的成员。在本文中,我使用静态变量,但这不是线程可重入的。 (2) :可以应用与(1)中相同的hack。
只有两种方法可以尝试:
您需要从模板化赋值运算符完全匹配的类型公开派生
提供对非模板复制运算符所需类型的非显式转换,以便使用
#include <iostream>
#include <tuple>
using namespace std;
struct X : tuple<int,int> {
};
struct Y {
int i;
operator tuple<int&,int&>() {return tuple<int&,int&>{i,i};}
};
int main()
{
int a, b;
tie(a, b) = make_tuple(9,9);
tie(a, b) = X{};
tie(a, b) = Y{};
cout << a << ' ' << b << '\n';
}
#包括
#包括
使用名称空间std;
结构X:元组{
};
结构{
int i;
运算符tuple(){返回tuple{i,i};}
};
int main()
{
INTA,b;
tie(a,b)=make_-tuple(9,9);
tie(a,b)=X{};
tie(a,b)=Y{};
不知道为什么当前的尝试失败了
std::tie(a,b)
生成一个std::tuple
。
此类型与std::tuple
等无关
std::tuple
s有几个赋值运算符:
- 默认赋值运算符,采用
std::tuple
- 具有类型参数pack
U..
的元组转换赋值运算符模板,该模板采用std::tuple
- 具有两个类型参数的成对转换赋值运算符模板
U1,U2
,它采用std::pair
对于这三个版本,存在复制和移动变体;将常量&
或和&
添加到它们所采用的类型中
赋值运算符模板必须从函数参数类型(即赋值表达式的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>();
};
structfoo{
INTA;
b串;
模板
运算符std::tuple();
};
但是,<代码的元素类型
struct Foo {
int a;
string b;
template<typename T, typename U>
operator std::tuple<T, U>();
};
struct Foo : tuple<const int&, const string&> {
int a;
string b;
Foo(int a, string b) :
tuple{std::tie(this->a, this->b)},
a{a}, b{b}
{}
};
int main()
{
Foo foo(0, "bar");
foo.a = 42;
long long a;
string b;
tie(a, b) = foo;
cout << a << ' ' << b << '\n';
}
42 bar
#include <tuple>
#include <string>
#include <iostream>
#include <functional>
using namespace std;
struct Foo {
int a;
string b;
template <template<typename ...Args> class tuple, typename ...Args>
operator tuple<Args...>() const {
return forward_as_tuple(get<Args>()...);
}
template <template<typename ...Args> class tuple, typename ...Args>
operator tuple<Args...>() {
return forward_as_tuple(get<Args>()...);
}
private:
// This is hacky, may be there is a way to avoid it...
template <typename T>
T get()
{ static typename remove_reference<T>::type i; return i; }
template <typename T>
T get() const
{ static typename remove_reference<T>::type i; return i; }
};
template <>
int&
Foo::get()
{ return a; }
template <>
string&
Foo::get()
{ return b; }
template <>
int&
Foo::get() const
{ return *const_cast<int*>(&a); }
template <>
string&
Foo::get() const
{ return *const_cast<string*>(&b); }
int main() {
Foo foo { 42, "bar" };
const Foo foo2 { 43, "gah" };
int a;
string b;
tie(a, b) = foo;
cout << a << ", " << b << endl;
tie(a, b) = foo2;
cout << a << ", " << b << endl;
}