Search code examples
c++c++20c++-conceptsfunction-templates-overloading

(non)ambiguous static overload within templated class


My class template NodeMaker has 3 static member function templates called create_node which are distinguished by their argument(s) using C++20 concepts. When calling NodeMaker<>::create_node(x) from main() everything works as I intended, but when calling create_node from another member function, GCC claims ambiguous overload, and I fail to understand why.

#include <utility>
#include <type_traits>

template<class F> concept NodeFunctionType = std::invocable<std::remove_reference_t<F>, int>;
template<class T> concept ExtracterType = requires { typename T::I_am_an_extracter; };

template<class T = int>
struct NodeMaker {
  template<class... Args>
  static constexpr auto create_node(Args&&... args) { return new T(std::forward<Args>(args)...); }

  template<NodeFunctionType DataMaker> requires (!ExtracterType<DataMaker>)
  static constexpr auto create_node(DataMaker&& data_maker) { return create_node(); } // line 13

  template<ExtracterType DataMaker> requires (!NodeFunctionType<DataMaker>)
  static constexpr auto create_node(DataMaker&& data_maker) { return create_node(); } // line 16

  void do_something() {
    const auto target = create_node(0);  //this does not work in gcc
  }
};

int main(const int argc, const char** argv) {
  const auto target = NodeMaker<>::create_node(0); //but this works
}

GCC errors out when compiling:

<source>: In member function 'void NodeMaker<T>::do_something()':
<source>:19:36: error: call of overloaded 'create_node(int)' is ambiguous
   19 |     const auto target = create_node(0);
<source>:10:25: note: candidate: 'static constexpr auto NodeMaker<T>::create_node(Args&& ...) [with Args = {int}]'
   10 |   static constexpr auto create_node(Args&&... args) { return new T(std::forward<Args>(args)...); }
<source>:13:25: note: candidate: 'static constexpr auto NodeMaker<T>::create_node(DataMaker&&) [with DataMaker = int]'
   13 |   static constexpr auto create_node(DataMaker&& data_maker) { return create_node(); }
<source>:16:25: note: candidate: 'static constexpr auto NodeMaker<T>::create_node(DataMaker&&) [with DataMaker = int]'
   16 |   static constexpr auto create_node(DataMaker&& data_maker) { return create_node(); }

But,

13 | static constexpr auto create_node(DataMaker&& data_maker)

cannot possibly match because int does not satisfy NodeFunctionType and

16 | static constexpr auto create_node(DataMaker&& data_maker)

cannot possibly match because int does not satisfy ExtracterType. Further, under no circumstances can both function templates match since their constraints are obviously mutually exclusive.

So my question would be: why can't GCC disambiguate the call from a member function (but can do it when called from outside the class)?

PS: see godbolt.


Solution

  • This seems to be a gcc bug. Here is the submitted bug report.

    It seems that gcc requires the call to the static method create_node to be explicitly qualified with NodeMaker<>:: even from inside the non-static member function do_something.

    You can confirm that this is the case by adding NodeMaker<>:: and you'll see that it then works in gcc.

    void do_something() {
    //----------------------vvvvvvvvvvvvv--------------->added this qualification
        const auto target = NodeMaker<>::create_node(0); //works now in gcc
      }
    

    Demo


    Note also that if you use NodeMaker<T>::create_node(0); from inside do_something then the issue will reappear.