使用 CRTP 模式时继承的模糊方法
Ambiguous method from inheritance when using a CRTP pattern
我正在定义一个 DoubleWrapper
class 继承自两个 CRTP 基础 classes,Ratioable
和 Divable
,它们都定义 operator/()
,签名不同:
T operator/(double const& scalar) const { return T(this->underlying().get() / scalar); }
double operator/(T const& other) const { return this->underlying().get() / other.get(); }
它们的区别在于 return 类型和参数类型。但是编译器抱怨 operator/() 是模棱两可的。请注意,构造函数是显式的,因此不存在从 double 到 DoubleWrapper 的模糊转换。
- 在 Visual Studio 2017 年它正在编译并且 运行 很好但是我在代码 "more than one operator "/" 匹配那些操作数:(...)" 的地方得到了一个工具提示使用。如果我重命名运算符使其成为常规方法 (
divide
(...)),我会收到编译错误:
错误 C2385:'divide' 的访问不明确
注意:可能是基础 'Divable' 中的 'divide'
注意:或者可能是基础 'Ratioable'
中的 'divide'
G++ 6.2 即使使用运算符版本也给我一个编译错误:
错误:对成员“operator/”的请求不明确
双 b = a / a;
^
注意:候选人是:double Ratioable::operator/(const T&) const [with T = DoubleWrapper]
双运算符/(T const& val) const { return this->underlying().get() / val.get(); }
^~~~~~~~
注意:T Divable::operator/(const double&) const [with T = DoubleWrapper]
T operator/(double const& val) const { return T(this->underlying().get()/ val); }
^~~~~~~~
C++ 允许具有相同名称的方法,只要它们具有不同的签名即可。哪里来的歧义?
测试代码:
DoubleWrapper a(10.);
double b = a / (a/2.); // Both operator/ should be called. I would expect b value to be 2.
源代码:
/* Curiously Recurring Template Pattern */
template <typename T, template<typename> class crtpType>
struct crtp
{
T& underlying() { return static_cast<T&>(*this); }
T const& underlying() const { return static_cast<T const&>(*this); }
};
/* Inheriting class can be divided by a scalar */
template<typename T>
struct Divable : crtp<T, Divable>
{
T operator/(double const& scalar) const { return T(this->underlying().get() / scalar); }
};
/* Inheriting class can be divided by itself */
template<typename T>
struct Ratioable : crtp<T, Ratioable>
{
double operator/(T const& other) const { return this->underlying().get() / other.get(); }
};
struct DoubleWrapper :
public Divable<DoubleWrapper>,
public Ratioable<DoubleWrapper>
{
explicit DoubleWrapper(double val) : val_(val) {}
double get() const { return val_; }
private:
double val_;
};
名称解析在重载解析之前完成。
DoubleWrapper
中没有 operator/
所以编译器会在它的基础 类 中寻找 operator/
并在两者中找到一个使名称模棱两可的(并且没有发生重载决议)。
您可以使用 using
:
解决名称解析
struct DoubleWrapper :
public Divable<DoubleWrapper>,
public Ratioable<DoubleWrapper>
{
using Divable<DoubleWrapper>::operator/;
using Ratioable<DoubleWrapper>::operator/;
explicit DoubleWrapper(double val) : val_(val) {}
double get() const { return val_; }
private:
double val_;
};
有趣的阅读:
https://en.wikipedia.org/wiki/Dominance_(C++)
http://en.cppreference.com/w/cpp/language/unqualified_lookup
我正在定义一个 DoubleWrapper
class 继承自两个 CRTP 基础 classes,Ratioable
和 Divable
,它们都定义 operator/()
,签名不同:
T operator/(double const& scalar) const { return T(this->underlying().get() / scalar); }
double operator/(T const& other) const { return this->underlying().get() / other.get(); }
它们的区别在于 return 类型和参数类型。但是编译器抱怨 operator/() 是模棱两可的。请注意,构造函数是显式的,因此不存在从 double 到 DoubleWrapper 的模糊转换。
- 在 Visual Studio 2017 年它正在编译并且 运行 很好但是我在代码 "more than one operator "/" 匹配那些操作数:(...)" 的地方得到了一个工具提示使用。如果我重命名运算符使其成为常规方法 (
divide
(...)),我会收到编译错误:
错误 C2385:'divide' 的访问不明确 注意:可能是基础 'Divable' 中的 'divide' 注意:或者可能是基础 'Ratioable'
中的 'divide'G++ 6.2 即使使用运算符版本也给我一个编译错误:
错误:对成员“operator/”的请求不明确 双 b = a / a; ^ 注意:候选人是:double Ratioable::operator/(const T&) const [with T = DoubleWrapper] 双运算符/(T const& val) const { return this->underlying().get() / val.get(); } ^~~~~~~~ 注意:T Divable::operator/(const double&) const [with T = DoubleWrapper] T operator/(double const& val) const { return T(this->underlying().get()/ val); } ^~~~~~~~
C++ 允许具有相同名称的方法,只要它们具有不同的签名即可。哪里来的歧义?
测试代码:
DoubleWrapper a(10.);
double b = a / (a/2.); // Both operator/ should be called. I would expect b value to be 2.
源代码:
/* Curiously Recurring Template Pattern */
template <typename T, template<typename> class crtpType>
struct crtp
{
T& underlying() { return static_cast<T&>(*this); }
T const& underlying() const { return static_cast<T const&>(*this); }
};
/* Inheriting class can be divided by a scalar */
template<typename T>
struct Divable : crtp<T, Divable>
{
T operator/(double const& scalar) const { return T(this->underlying().get() / scalar); }
};
/* Inheriting class can be divided by itself */
template<typename T>
struct Ratioable : crtp<T, Ratioable>
{
double operator/(T const& other) const { return this->underlying().get() / other.get(); }
};
struct DoubleWrapper :
public Divable<DoubleWrapper>,
public Ratioable<DoubleWrapper>
{
explicit DoubleWrapper(double val) : val_(val) {}
double get() const { return val_; }
private:
double val_;
};
名称解析在重载解析之前完成。
DoubleWrapper
中没有 operator/
所以编译器会在它的基础 类 中寻找 operator/
并在两者中找到一个使名称模棱两可的(并且没有发生重载决议)。
您可以使用 using
:
struct DoubleWrapper :
public Divable<DoubleWrapper>,
public Ratioable<DoubleWrapper>
{
using Divable<DoubleWrapper>::operator/;
using Ratioable<DoubleWrapper>::operator/;
explicit DoubleWrapper(double val) : val_(val) {}
double get() const { return val_; }
private:
double val_;
};
有趣的阅读:
https://en.wikipedia.org/wiki/Dominance_(C++)
http://en.cppreference.com/w/cpp/language/unqualified_lookup