Why has this C++ code an ambiguous method call only on Microsoft compiler?
Asked Answered
T

1

13

I'm trying to compile a library on microsoft C++ compiler 14.1 (Visual Studio 2017) but I'm getting a strange error due to an ambiguous call to a class method. After some testing I isolated the following code snippet:

#include <iostream>

struct Event
{};

template<typename Derived>
struct State
{
public:
    template<typename Fsm>
    void onEvent(Fsm& fsm, const Event& event)
    {
        std::cout << "State::onEvent\n";
    }

};

struct DerivedState
    : State<DerivedState>
{
public:
    using State::onEvent;

    template<typename Fsm>
    void onEvent(Fsm& fsm, const Event& event)
    {
        std::cout << "DerivedState::onEvent\n";
    }

};

struct Context
{};


int main()
{
    DerivedState ds;
    Context context;
    ds.onEvent(context, Event());
}

I get the following output:

1>c:\users\pmas\documents\visual studio

2017\projects\consoleapplication3\consoleapplication3\consoleapplication3.cpp(87): error C2668: 'DerivedState::onEvent': ambiguous call to overloaded function
1>c:\users\pmas\documents\visual studio 2017\projects\consoleapplication3\consoleapplication3\consoleapplication3.cpp(59): note: could be 'void DerivedState::onEvent<Context>(Fsm &,const Event &)'
1>        with
1>        [
1>            Fsm=Context
1>        ]
1>c:\users\pmas\documents\visual studio 2017\projects\consoleapplication3\consoleapplication3\consoleapplication3.cpp(45): note: or       'void State<DerivedState>::onEvent<Context>(Fsm &,const Event &)'
1>        with
1>        [
1>            Fsm=Context
1>        ]
1>c:\users\pmas\documents\visual studio 2017\projects\consoleapplication3\consoleapplication3\consoleapplication3.cpp(87): note: while trying to match the argument list '(Context, Event)'

The code looks to me perfectly legal and compiles fine on gcc, clang and icc (and behaves as expected, too).

After some additional testing, I find out that the code compiles fine if I avoid the CRTP pattern in deriving DerivedState:

#include <iostream>

struct Event
{};

struct State
{
public:
    template<typename Fsm>
    void onEvent(Fsm& fsm, const Event& event)
    {
        std::cout << "State::onEvent\n";
    }

};

struct DerivedState
    : State
{
public:
    using State::onEvent;

    template<typename Fsm>
    void onEvent(Fsm& fsm, const Event& event)
    {
        std::cout << "DerivedState::onEvent\n";
    }

};

struct Context
{};


int main()
{
    DerivedState ds;
    Context context;
    ds.onEvent(context, Event());
}

Can anybody explain this difference? Is microsoft compiler not conforming to the standard?

Trencher answered 12/7, 2017 at 12:7 Comment(0)
N
7

According to the cppreference documentation (emphasis mine)

Using-declaration introduces a member of a base class into the derived class definition, such as to expose a protected member of base as public member of derived. In this case, nested-name-specifier must name a base class of the one being defined. If the name is the name of an overloaded member function of the base class, all base class member functions with that name are introduced. If the derived class already has a member with the same name, parameter list, and qualifications, the derived class member hides or overrides (doesn't conflict with) the member that is introduced from the base class.

So basically your using declaration for the method does not really do anything (the method was public anyway, so no need for the using). Your derived class simply hides the onEvent method.

So the other compilers are right, and microsoft is wrong.

Nela answered 12/7, 2017 at 12:16 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.