【问题标题】:Having class inherit constructors of template type让类继承模板类型的构造函数
【发布时间】:2021-01-20 13:05:10
【问题描述】:

我创建了以下类来创建任何类型的值,这些值在每次调用运算符时都是固定的或重新计算的:

template <typename T>
class DynamicValue {
    private:
    std::variant<T, std::function<T()>> getter;

    public:
    DynamicValue(const T& constant) : getter(constant){};
    template <typename F, typename = std::enable_if_t<std::is_invocable_v<F>>>
    DynamicValue(F&& function) : getter(function) {}
    DynamicValue(const T* pointer) : DynamicValue([pointer]() { return *pointer; }) {}
    DynamicValue(const DynamicValue& value) : getter(value.getter) {}
    ~DynamicValue() {}
    DynamicValue& operator=(const DynamicValue& value) {
        getter = value.getter;
        return *this;
    }
    T operator()() {
        return getter.index() == 0 ? std::get<T>(getter) : std::get<std::function<T()>>(getter)();
    }
};

我还编写了以下虚拟结构来展示我的问题:

struct A {
    int b;
};

问题是,理想情况下,我可以初始化任何DynamicValue&lt;T&gt;,就好像它是T 类型一样。所以,在这个例子中,因为我可以写A a = {1};,而不是写DynamicValue&lt;A&gt; a = A{1};,我可以写DynamicValue&lt;A&gt; a = {1};。但是,当我尝试这样做时,出现以下错误:

无法将“{1}”从“”转换为“动态值”

您可以尝试一个现场示例here

有没有办法克服这个问题,还是我必须接受更长的语法?

【问题讨论】:

  • = {1} 是聚合的初始化语法,不可用于非聚合类。相反,您应该使用统一的初始化语法{1}
  • 带括号的初始化列表没有类型,因此很难在模板上下文中使用。

标签: c++ templates constructor


【解决方案1】:

您可以添加一个构造函数,该构造函数接受 T 类型所需的所有参数并将它们转发给 T 的构造函数并初始化您的内部值 getter

只需将以下行添加到您的类中:

template < typename ... S> DynamicValue( S&& ... parms ): getter{T{ std::forward<S...>( parms... )}} {}

但是!如果我们这样做,如果我们传递一个非 const DynamicValue,它也会被调用。所以我们必须通过 SFINAE 排除它并得到:

template <typename F,
   typename = std::enable_if_t<std::is_invocable_v<F>&&    !std::is_same_v<std::remove_reference_t<F>, DynamicValue<T>>, bool>>
            DynamicValue(F&& function) : getter(function) {}

但是还有一个问题:

您的代码已经有一些可以作为非预期的复制构造函数调用的东西!

构造函数:

template <typename F, typename = std::enable_if_t<std::is_invocable_v<F>>>
    DynamicValue(F&& function) : getter(function) {}

也将被调用,因为DynamicType&lt;T&gt; 也是可调用的!我们还必须使用第二个条件来禁用 DynamicType&lt;T&gt; 类型的实例化,这会导致:

template <typename F, typename = std::enable_if_t<std::is_invocable_v<F>&& !std::is_same_v<std::remove_reference_t<F>, DynamicValue<T>>, bool>>
            DynamicValue(F&& function) : getter(function) {}

您可以使用以下方法创建实例:

DynamicValue<A> a1 = A{1};
DynamicValue<A> a2{1};

使用= {1} 将不起作用。

现在甚至是片段

DynamicValue<A> a9{100};
DynamicValue<A> a10(a9);
std::cout << a10().b << std::endl;

按预期工作。

【讨论】:

  • 你肯定想约束这个模板。否则它将被用作左值的“复制构造函数”。
  • @NathanOliver:现在复制和移动构造函数已经到位,模板将不再用于复制/移动操作。我是对的?
  • 如果您先执行DynamicValue&lt;A&gt; a;,然后执行DynamicValue&lt;A&gt; a(b),您的转发构造函数将被调用,因为它将完全匹配a 的类型,其中复制构造函数将执行常量转换和移动构造函数无法绑定到左值。
猜你喜欢
  • 2019-12-14
  • 1970-01-01
  • 2016-03-04
  • 2019-12-14
  • 2015-08-06
  • 2018-09-29
  • 2013-09-04
  • 1970-01-01
  • 2021-07-26
相关资源
最近更新 更多