为什么调用具有通用或 r 值引用的重载覆盖函数是不明确的?

Why call to overloaded overridden function with universal or r-value reference is ambiguous?

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

问题场景

我有两个 class 用来表示模型的变量。通用的是 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) 允许左值成为调用 MState::setValue 时的有效参数。实际上,如果我删除它,编译器会抱怨它无法将左值转换为右值引用(我理解为 []。)但是,我想避免不必要的资源分配和复制,尤其是在处理临时对象时(例如 ms.setValue(MyClass())。)因此 (B).

同样,(C) 是必需的,因为除了 MState 的实现之外,我还需要 MOutput 做更多的事情。同样的动机,我也想要(D)。

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

问题

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

用例

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

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/

我相信在这种情况下,它无法判断是应该复制 T 还是通过右值引用将其传入,因此存在歧义。尝试为第一个函数获取对 T 的 const 引用。

此外,std::forward 适用于进行通用引用(推导上下文中的右值引用),我认为它不适用于此处。

我只需要简单的 void setValue(const T& value)(当您的 getter 复制时甚至更多)。

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

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();
};

Is T&& here a universal reference?

作为声明,在评论中,不,它不是,T 由 class 修复。