避免在转换运算符中复制到基类的子集

时间:2019-04-11 12:43:48

标签: c++ inheritance c++17 conversion-operator

首先,您将大喊“ XY problem!”并且您将是对的,但是现在,我正在尝试查看此特定Y相对于其他(较大)X与其他Y相比,是否有很好的解决方案来判断/最小化其折衷。


考虑以下可变参数模板类,该可变参数模板类从其所有模板参数继承,并为它们的子集提供转换运算符:

template <typename... Ts>
struct derived : Ts...
{
    template<class... SubTs>
    operator const derived<SubTs...>()
    {
        return { static_cast<SubTs>(*this)... };
    }
};

这允许您执行以下操作:

struct A { int a; };
struct B { double b; };
struct C { std::unique_ptr<int> c; };

using ABC = derived<A, B, C>;

int foo(const derived<A, B>& in)
{
    return in.a + in.b;
}

int test()
{
    ABC abc{ {1}, {3.0}, {std::make_unique<int>(4)}};
    return foo(x);
}

该转换确实会创建一个临时副本以传递给函数。因此,您无法执行以下操作,因为无法复制unique_ptr

int bar(const derived<A, C>& in)
{
    return in.a + *in.c;
}

int test()
{
    ABC abc { {1}, {3.0}, {std::make_unique<int>(4)}};
    return bar(x);
}

实时代码:https://godbolt.org/z/KBoBDl

有解决方案吗?

  1. 我可以传递不可复制的derived(当然不会放弃所有权)。

  2. 我不必指定A的给定成员来自B / C / in中的哪个(无in.get<A>().a) 。 也不是“使用合成而不是继承”。换句话说,应该保留in.ain->a也可以)的语义。

  3. foobar的函数自变量在单个可变参数模板列表中阐明了它们体内可用的子类型(不一定必须位于{{1 }},尽管我认为之前的要求会强制这样做。如果我没有提到derived,则该函数不应访问B的成员(即使传入的B包含derived)。

B / foo更改为模板函数将是一个挫折,但可以接受。请注意,bar(并删除转换运算符)违反了最后一个要求。另请注意,我在C ++ 17中,因此没有概念。

理想情况下,我会想象函数签名看起来像template<class T> int foo(T in),但我看不出如何将其与所有要求统一。欢迎提供部分想法/解决方案,也许有帮助。

int bar(derived_view<A, C> in) 是我最好的构图,derived / A / {{1} }已经是所有包装器结构。这就是为什么不应再有其他间接方式访问实际数据。


是的,我知道,“这听起来像是带有额外步骤的常规函数​​参数!” ,但是我有很多BC,它们包含许多不同的子集数十个foo / bar / A等结构中,每个结构都建立更多这样的结构。普通函数参数是经过充分研究的现状,并不是令人满意的解决方案。

1 个答案:

答案 0 :(得分:0)

如果我们将derived_view限制为“只能用作函数参数”,那么将使用过的所有参数从传递的derived(或derived_view)移到其构造函数,然后将其移回析构函数:

template <typename... Ts>
struct derived_view : Ts...
{
    template<class... SubTs>
    derived_view(derived<SubTs...>& original)
      : Ts(static_cast<Ts&&>(original))...
    {
        _restoreOriginal = [&](){
            ((static_cast<Ts&>(original) = static_cast<Ts&&>(*this)), ...);
        };
    }

    // Also allow construction from another derived_view
    template<class... SubTs>
    derived_view(derived_view<SubTs...>& original)
      : Ts(static_cast<Ts&&>(original))...
    {
        _restoreOriginal = [&](){
            ((static_cast<Ts&>(original) = static_cast<Ts&&>(*this)), ...);
        };
    }

    ~derived_view()
    {
        _restoreOriginal();
    }

private:
    std::function<void()> _restoreOriginal;
};

演示:https://wandbox.org/permlink/CVkmZc7AXSkTiJZx

如果有人要使用derived_view并同时访问从其构造的derived(或derived_view),显然这很火。将其移交给其他用户(这是此处的计划)很可能迟早会导致灾难……