c++ variant class 成员通过引用存储
c++ variant class member stored by reference
我正在尝试 std::variant。我将 std::variant 存储为 class 的成员。在下面的代码中,如果变体按值存储,则一切正常,但如果变体按引用存储,则不起作用(对于向量情况,以及自定义对象)。这是为什么?
#include <variant>
#include <vector>
#include <iostream>
template<typename T>
using VectorOrSimple = std::variant<T, std::vector<T>>;
struct Print {
void operator()(int v) { std::cout << "type = int, value = " << v << "\n"; }
void operator()(std::vector<int> v) const { std::cout << "type = vector<int>, size = " << v.size() << "\n"; }
};
class A {
public:
explicit A(const VectorOrSimple<int>& arg) : member(arg) {
print();
}
inline void print() const {
visit(Print{}, member);
}
private:
const VectorOrSimple<int> member; // const VectorOrSimple<int>& member; => does not work
};
int main() {
int simple = 1;
A a1(simple);
a1.print();
std::vector<int> vector(3, 1);
A a2(vector);
a2.print();
}
请参阅 http://melpon.org/wandbox/permlink/vhnkAnZhqgoYxU1H for a working version, and http://melpon.org/wandbox/permlink/T5RCx0ImTLi4gk5e 以了解崩溃版本并出现错误:“在抛出 'std::bad_variant_access' 的实例后调用终止
what(): 意外索引
奇怪的是,当编写代码的 boost::variant 版本并将成员存储为参考时,它可以使用 gcc7.0 按预期工作(打印向量大小 = 3 两次)(参见此处 http://melpon.org/wandbox/permlink/eW3Bs1InG383vp6M) and does not work (prints vector size = 3 in constructor and then vector size = 0 on the subsequent print() call, but no crash) with clang 4.0 (see here http://melpon.org/wandbox/permlink/2GRf2y8RproD7XDM).
这很令人困惑。有人可以解释发生了什么吗?
谢谢
它不起作用,因为此语句 A a1(simple);
创建了一个临时变体对象!
然后您继续将所述临时绑定到您的 const 引用。但是在 a1
的构造结束后,临时变量立即超出范围,留下一个悬空的引用。显然,创建副本是有效的,因为它总是涉及使用有效副本。
一个可能的解决方案(如果总是复制的性能让您担心)是按值接受变体对象,然后将其移动到您的本地副本中,如下所示:
explicit A(VectorOrSimple<int> arg) : member(std::move(arg)) {
print();
}
这将允许使用左值或右值调用构造函数。对于左值,您的 member
将通过移动源变体的副本来初始化,对于 rvalues
,源的内容将(最多)移动两次。
变体是对象。它们包含一组类型中的一种,但它们不是这些类型中的一种。
对变体的引用是对变体对象的引用,而不是对其中一种包含类型的引用。
参考包装器的变体可能是您想要的:
template<class...Ts>
using variant_ref=std::variant<std::reference_wrapper<Ts>...>;
template<typename T>
using VectorOrSimple = std::variant<T, std::vector<T>>;
template<typename T>
using VectorOrSimpleRef = variant_ref<T, std::vector<T>>;
template<typename T>
using VectorOrSimpleConstRef = variant_ref<const T, const std::vector<T>>;
现在存储VectorOfSimpleConstRef<int>
。 (不是 const&
)。并在构造函数中也取一个。
同时修改 Print
以在 const&
之前使用以避免在打印时不必要地复制 std::vector
。
我正在尝试 std::variant。我将 std::variant 存储为 class 的成员。在下面的代码中,如果变体按值存储,则一切正常,但如果变体按引用存储,则不起作用(对于向量情况,以及自定义对象)。这是为什么?
#include <variant>
#include <vector>
#include <iostream>
template<typename T>
using VectorOrSimple = std::variant<T, std::vector<T>>;
struct Print {
void operator()(int v) { std::cout << "type = int, value = " << v << "\n"; }
void operator()(std::vector<int> v) const { std::cout << "type = vector<int>, size = " << v.size() << "\n"; }
};
class A {
public:
explicit A(const VectorOrSimple<int>& arg) : member(arg) {
print();
}
inline void print() const {
visit(Print{}, member);
}
private:
const VectorOrSimple<int> member; // const VectorOrSimple<int>& member; => does not work
};
int main() {
int simple = 1;
A a1(simple);
a1.print();
std::vector<int> vector(3, 1);
A a2(vector);
a2.print();
}
请参阅 http://melpon.org/wandbox/permlink/vhnkAnZhqgoYxU1H for a working version, and http://melpon.org/wandbox/permlink/T5RCx0ImTLi4gk5e 以了解崩溃版本并出现错误:“在抛出 'std::bad_variant_access' 的实例后调用终止 what(): 意外索引
奇怪的是,当编写代码的 boost::variant 版本并将成员存储为参考时,它可以使用 gcc7.0 按预期工作(打印向量大小 = 3 两次)(参见此处 http://melpon.org/wandbox/permlink/eW3Bs1InG383vp6M) and does not work (prints vector size = 3 in constructor and then vector size = 0 on the subsequent print() call, but no crash) with clang 4.0 (see here http://melpon.org/wandbox/permlink/2GRf2y8RproD7XDM).
这很令人困惑。有人可以解释发生了什么吗? 谢谢
它不起作用,因为此语句 A a1(simple);
创建了一个临时变体对象!
然后您继续将所述临时绑定到您的 const 引用。但是在 a1
的构造结束后,临时变量立即超出范围,留下一个悬空的引用。显然,创建副本是有效的,因为它总是涉及使用有效副本。
一个可能的解决方案(如果总是复制的性能让您担心)是按值接受变体对象,然后将其移动到您的本地副本中,如下所示:
explicit A(VectorOrSimple<int> arg) : member(std::move(arg)) {
print();
}
这将允许使用左值或右值调用构造函数。对于左值,您的 member
将通过移动源变体的副本来初始化,对于 rvalues
,源的内容将(最多)移动两次。
变体是对象。它们包含一组类型中的一种,但它们不是这些类型中的一种。
对变体的引用是对变体对象的引用,而不是对其中一种包含类型的引用。
参考包装器的变体可能是您想要的:
template<class...Ts>
using variant_ref=std::variant<std::reference_wrapper<Ts>...>;
template<typename T>
using VectorOrSimple = std::variant<T, std::vector<T>>;
template<typename T>
using VectorOrSimpleRef = variant_ref<T, std::vector<T>>;
template<typename T>
using VectorOrSimpleConstRef = variant_ref<const T, const std::vector<T>>;
现在存储VectorOfSimpleConstRef<int>
。 (不是 const&
)。并在构造函数中也取一个。
同时修改 Print
以在 const&
之前使用以避免在打印时不必要地复制 std::vector
。