为什么调用具有通用或 r 值引用的重载覆盖函数是不明确的?
Why call to overloaded overridden function with universal or r-value reference is ambiguous?
我有一个与引用转发相关的问题,对此我只是略微熟悉。在陈述我的问题之前,我想说我已经阅读了几页与此语言功能相关的内容,它们让我感到困惑,而不是阐明我的问题。查阅的参考文献有:[1-5],其中有一些我觉得矛盾或者不完全理解。意识到这个问题可能被认为是重复的事实,我还是发布了它,因为我找不到可以结合这三个因素的案例:
- 具有 r 值引用的重载函数。
- 模板化 classes(不是函数)。
- 覆盖重载函数,其中父 class' 函数在覆盖中被调用。
问题场景
我有两个 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.
参考资料
我相信在这种情况下,它无法判断是应该复制 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 修复。
我有一个与引用转发相关的问题,对此我只是略微熟悉。在陈述我的问题之前,我想说我已经阅读了几页与此语言功能相关的内容,它们让我感到困惑,而不是阐明我的问题。查阅的参考文献有:[1-5],其中有一些我觉得矛盾或者不完全理解。意识到这个问题可能被认为是重复的事实,我还是发布了它,因为我找不到可以结合这三个因素的案例:
- 具有 r 值引用的重载函数。
- 模板化 classes(不是函数)。
- 覆盖重载函数,其中父 class' 函数在覆盖中被调用。
问题场景
我有两个 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.
参考资料
我相信在这种情况下,它无法判断是应该复制 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 修复。