gg99
gg99

Reputation: 656

Ambiguous method from inheritance when using a CRTP pattern

I am defining a DoubleWrapper class inheriting from two CRTP base classes, Ratioable and Divable, that both define operator/(), with different signatures:

T operator/(double const& scalar) const { return T(this->underlying().get() / scalar); }
double operator/(T const& other) const { return this->underlying().get() / other.get(); }

They differ both by return type and parameter type. However compiler is complaining about operator/() being ambiguous. Note that the constructor is explicit so there is no ambiguous conversion from double to DoubleWrapper.

error C2385: ambiguous access of 'divide' note: could be the 'divide' in base 'Divable' note: or could be the 'divide' in base 'Ratioable'

C++ allows methods with the same name as long as they have different signatures. Where is the ambiguity coming from ?

Test code:

    DoubleWrapper a(10.);
    double b  = a / (a/2.); // Both operator/ should be called. I would expect b value to be 2.

Source code:

    /* 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_;
};

Upvotes: 3

Views: 561

Answers (1)

Jarod42
Jarod42

Reputation: 217265

Name resolution is done before overload resolution.

There are no operator/ in DoubleWrapper so the compiler goes looking for operator/ in it's base classes and finds one in both making the name ambiguous (and no overload resolution takes place).

You may resolve name resolution with 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_;
};

Interesting reading:

https://en.wikipedia.org/wiki/Dominance_(C++)

http://en.cppreference.com/w/cpp/language/unqualified_lookup

Upvotes: 3

Related Questions