通过引用存储的c ++变体类成员

时间:2016-12-14 09:33:56

标签: c++ boost c++17 variant

我正在尝试使用std :: variant。我将std :: variant存储为类的成员。在下面的代码中,如果变量按值存储,则工作正常,但如果变量通过引用存储,则不起作用(对于矢量大小写,也适用于自定义对象)。那是为什么?

#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,有关错误的崩溃版本请参阅http://melpon.org/wandbox/permlink/T5RCx0ImTLi4gk5e:“抛出'std :: bad_variant_access'实例后调用终止   what():意外索引“

奇怪的是,当编写一个boost :: variant版本的代码并将该成员存储为引用时,它可以按预期工作(使用gcc7.0打印矢量大小= 3两次)(参见此处http://melpon.org/wandbox/permlink/eW3Bs1InG383vp6M)和不起作用(在构造函数中打印向量大小= 3,然后在随后的print()调用中向量大小= 0,但没有崩溃)与clang 4.0(请参阅此处http://melpon.org/wandbox/permlink/2GRf2y8RproD7XDM)。

这令人困惑。有人可以解释发生了什么吗? 感谢。

2 个答案:

答案 0 :(得分:5)

它不起作用,因为此语句A a1(simple);创建了一个临时变体对象!

然后继续将所述临时绑定到const引用。但是a1的构建结束后临时超出了范围,留下了一个悬空参考。显然,创建副本有效,因为它总是涉及使用有效副本。

一种可能的解决方案(如果总是复制的性能让你担心)是接受变量对象的值,然后将其移动到本地副本中,如下所示:

explicit A(VectorOrSimple<int> arg) : member(std::move(arg)) {
    print();
}

这将允许使用lvalues或rvalues调用构造函数。对于左值,您的member将通过移动源变体的副本进行初始化,对于rvalues,源的内容将被移动(最多)两次。

答案 1 :(得分:1)

变体是对象。它们包含一组类型中的一种,但它们不是这些类型中的一种。

对变体的引用是对变体对象的引用,而不是对其中一个包含类型的引用。

参考包装器的变体可能是您想要的:

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