为什么用通用或r值引用调用重载的重写函数是模棱两可的?

时间:2018-06-22 14:57:31

标签: c++ c++11 rvalue-reference forwarding-reference

我有一个与参考转发有关的问题,对此我只是略有熟悉。在陈述我的问题之前,我想说的是,我已经阅读了与该语言功能相关的几页,它们使我感到困惑,而不是让我陷入困境。参考的参考文献是:[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的调用中的有效参数。实际上,如果我删除它,编译器会抱怨它无法将l值转换为r值引用(我用[{4]理解。)但是,我想避免不必要的资源分配和复制,尤其是与临时人员打交道时(例如ms.setValue(MyClass())。)因此是(B)。

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

[3]促使在(C)和(D)中使用std::forward<T>(value)

问题

当我尝试编译代码时,GCC抱怨重载的函数签名在两个类和许多情况下都模棱两可。我想我不明白为什么或如何解决这个问题。 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/
  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

2 个答案:

答案 0 :(得分:1)

我相信在这种情况下,它无法确定是复制T还是通过右值引用将其传递,从而造成歧义。尝试对第一个函数采用对T的const引用。

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

答案 1 :(得分:1)

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

但是,如果您既想要l值引用又要r值引用,它将类似于:

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由类固定。