如何使用std :: variant保证复制省略?

时间:2019-06-04 00:12:19

标签: c++ c++17 variant copy-elision

我有这种类型:

struct immobile {
   // other stuff omitted
   immobile(immobile&) = delete;
   immobile(immobile&&) = delete;
};
immobile mk_immobile();
// e.g. this compiles
// mk_immobile() is a prvalue and i is its result object
immobile i(mk_immobile());

我也有这个课程模板:

template<typename T>
struct container {
    std::variant<T, other_stuff> var;
    template<typename... Args>
    container(Args&&... args)
    : var(std::in_place_index<0>, std::forward<Args>(args)...) {}
};

我想围绕container产生的对象构造一个mk_immobile(),并使用immobile对象初始化var的一种变体。

container<immobile> c(mk_immobile());

但是,这不起作用。例如,std::variant的构造函数需要std::is_constructible_v<immobile, immobile>,但不成立。更糟糕的是,即使这个简化的版本也失败了:

template<typename T>
struct demonstration {
    T t;
    template<typename... Args>
    demonstration(Args&&... args) : t(std::forward<Args>(args)...) {}
};
demonstration<immobile> d(mk_immobile());

这似乎暗示std::forward实际上并不完美地转发-pr值不会作为pr值转发。 (这对我来说很有意义;我认为这样做不可能。)我可以通过将demonstration更改为以下内容来使其工作:

template<typename T>
struct demonstration {
    T t;
    template<typename F>
    demonstration(F&& f) : t(std::forward<F>(f)()) {}
};
demonstration<immobile> d([] { return mk_immobile(); });

但是我没有看到以类似方式更改container的方法。如何更改container,以便它可以从prvalue构造一个std::variant(或其他带标签的并集)?我可以更改container,但不能更改immobile

1 个答案:

答案 0 :(得分:1)

您滥用演员表

template<typename F>
struct initializer
{
    F f;
    template<typename T>
    operator T()
    {
        return f();
    }
};

template<typename F>
initializer(F&&) -> initializer<F>;

并用作

container<immobile> c{initializer{[]{
    return mk_immobile();
}}};