4

我有一个与引用转发有关的问题,对此我只是稍微熟悉。在陈述我的问题之前,我想说我已经阅读了一些与此语言功能相关的页面,它们让我感到困惑,而不是阐明我的问题。参考的参考文献是:[ 1 - 5 ],其中一些我觉得矛盾或不完全理解。意识到这个问题可能被认为是重复的事实,我还是发布了它,因为我找不到一个可以结合这三个因素的案例:

  • 具有 r 值引用的重载函数。
  • 模板类(不是函数)。
  • 在覆盖中调用父类的函数的重载函数的覆盖。

问题场景

我有两个类用于表示模型的变量。通用的是MState<T>,我将它模板化,因为我希望它包含任何可能的类型。它更像是我为与此问题无关的目的而创建的类型的包装器。然后,我有MOutput<T>,它本质上扩展MState<T>并表示用户定义模型的外部可见变量。

它们都有一个名为的成员函数setValue,它允许,嗯……设置它们的值。我知道实现运算符语义会很好,但现在我正在处理这个:

template <class T>
class MState
{
public:
    virtual void setValue(T value) {      // -- (A)
        m_state = value;
        // (... more things.)
    }
    virtual void setValue(T&& value) {    // -- (B)
        m_state = value;
        // (... more things.)
    }
    T getValue(void) { return m_state; }
protected:
    T m_state;
};

template <class T>
class MOutput : public MState<T>
{
public:
    void setValue(T value) override {                     // -- (C)
        MState<T>::setValue(std::forward<T>(value));
        // (... even more things.)
    }
    void setValue(T&& value) override {                   // -- (D)
        MState<T>::setValue(std::forward<T>(value));
        // (... even more things.)
    }
};

我认为我需要 (A) 允许 l 值成为调用中的有效参数MState::setValue。实际上,如果我删除它,编译器会抱怨它无法将左值引用转换为右值引用(我理解为 [ 4 ]。)但是,我想避免不必要的资源分配和复制,尤其是在处理临时文件时(例如ms.setValue(MyClass())。)因此(B)。

同样, (C) 是必要的,因为MOutput除了那些工具之外,我还需要 's 做更多的事情MState。对于上述相同的动机,我也想要(D)。

std::forward<T>(value)(C) 和 (D) 中的使用是由 [ 3 ] 推动的。

问题

当我尝试编译我的代码时,GCC 抱怨重载的函数签名在两个类和许多情况下都是模棱两可的。我想我不明白为什么,或者如何解决这个问题。T&&这里是通用参考吗?如果是这样,这些不应该接受任何类型的参数类型吗?

用例

GCC 无法编译MState<T>::setValue(std::forward<T>(value));(C) 和 (D) 的行。如果我将它们注释掉,在这些情况下我会得到同样的歧义错误:

bool function(int a) { return a >= 1; }
MOutput<bool> vo0, vo1, vo2;
vo1.setValue(true);                                // Error.
vo2.setValue(false);                               // Error.
vo0.setValue(!(vo1.getValue() & vo2.getValue()));  // Error.

MOutput<int> vo3;
vo3.setValue(432);                                 // Error.

参考文献

  1. https://www.codesynthesis.com/~boris/blog/2012/06/26/efficient-argument-passing-cxx11-part2/
  2. https://stackoverflow.com/a/31551595/1876268
  3. https://stackoverflow.com/a/3582313/1876268
  4. https://stackoverflow.com/a/40350289/1876268
  5. https://stackoverflow.com/a/5465371/1876268
4

2 回答 2

1

我只想简单void setValue(const T& value)(当你的吸气剂复制时更简单)。

但是如果你想要左值引用和右值引用,它会是这样的:

template <class T>
class MState
{
public:
    virtual void setValue(const T& value) {
        m_state = value;
        MoreStuff()
    }
    virtual void setValue(T&& value) {
        m_state = std::move(value);
        MoreStuff()
    }
    const T& getValue() const { return m_state; }
private:
    void MoreStuff();
protected:
    T m_state;
};

template <class T>
class MOutput : public MState<T>
{
public:
    void setValue(const T& value) override {
        MState<T>::setValue(value);
        MoreThings();
    }
    void setValue(T&& value) override {
        MState<T>::setValue(std::move(value));
        MoreThings();
    }
private:
    void MoreThings();
};

T&& 这里是通用参考吗?

作为状态,在评论中,不,它不是,T由班级固定。

于 2018-06-22T17:03:03.080 回答
1

我相信在这种情况下,它无法判断是否应该制作 T 的副本,或者通过右值引用传递它,从而产生歧义。尝试为第一个函数获取对 T 的 const 引用。

std::forward 也适用于当您进行通用引用(推导上下文中的右值引用)时,我认为它不适用于这里。

于 2018-06-22T16:40:45.543 回答