c++ - 使用 CRTP 模式时继承的模糊方法

标签 c++ multiple-inheritance crtp ambiguous

我正在定义一个 DoubleWrapper 类,它继承自两个 CRTP 基类,RatioableDivable,它们都定义了 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(); }

它们在返回类型和参数类型上都有所不同。但是编译器提示 operator/() 是模棱两可的。请注意,构造函数是显式的,因此不存在从 double 到 DoubleWrapper 的模糊转换。

  • 在 Visual Studio 2017 上,它可以正常编译和运行,但是我在代码中得到一个工具提示“不止一个运算符”/“匹配这些操作数:(...)”在使用位置。如果我重命名运算符以使其成为常规方法 (divide(...)),我会收到编译错误:

error 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/ 并在两者中找到一个名称不明确(并且没有发生重载解析)。

您可以通过使用来解析名称:

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

关于c++ - 使用 CRTP 模式时继承的模糊方法,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/46912649/

相关文章:

c++ - argc 和 argv 的问题

python - Python C API 中的多重继承

c++ - C++03 后期绑定(bind)接口(interface)的设计模式

c++ - 使用 static_cast 实现的转换运算符

c++ - std::declval vs crtp,无法从不完整类型推断方法返回类型

c++ - 与 std::lower_bound 相比,ranges::lower_bound 是否有不同的比较要求?

c++ - 在模板中使用二叉树作为优先级队列

c++ - 罗伯特·吉塞克的非管理导出

c++ - 在多重继承的情况下选择 vptr

c# - 是C#编译器还是CLR禁止多重继承