1

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.

Jason
  • 36,170
  • 5
  • 26
  • 60
igel
  • 358
  • 1
  • 8
  • Looks like a GCC bug. I wasn't able to find a matching bug report on a quick search. – user17732522 Jun 21 '22 at 13:26
  • @user17732522 Yes, i agree. I am submitting a bug report now. – Jason Jun 21 '22 at 13:27
  • @user17732522 For some reason the gcc bug tracker is not letting create a new account. Would you be interested in submitting the bug and sharing the link. I will add it(that shared link) in the answer. – Jason Jun 21 '22 at 13:43
  • @AnoopRana I don't have an account either and not really time right now to figure it out. If someone else wants to report, they can feel free to take the test case I posted above. – user17732522 Jun 21 '22 at 13:45
  • @user17732522 Sure. – Jason Jun 21 '22 at 13:45
  • Actually, better test case not relying on standard library: https://godbolt.org/z/MhK3vh9jd – user17732522 Jun 21 '22 at 13:49
  • https://gcc.gnu.org/bugzilla/show_bug.cgi?id=105842 – 康桓瑋 Jun 21 '22 at 14:02
  • @康桓瑋 Yeah, thanks for finding the issue. I added the link in my answer so that if the comments get deleted(for whatever reason) the link will still be there in the answer. – Jason Jun 21 '22 at 14:05
  • @user17732522 The example that you gave does work with gcc 11.2. [Demo](https://godbolt.org/z/39GEd7o8v) – Jason Jun 21 '22 at 16:22
  • @AnoopRana Hm, seems that there are several things wrong with GCC. That variant is then actually a regression. It happens only on trunk. If I use a concept instead, then it seems to happen in all versions: https://godbolt.org/z/GKjG31he7 – user17732522 Jun 21 '22 at 16:45
  • @user17732522 A new/separate bug has been submitted [here](https://gcc.gnu.org/bugzilla/show_bug.cgi?id=106046). – Jason Jun 21 '22 at 16:47

1 Answers1

1

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.

Jason
  • 36,170
  • 5
  • 26
  • 60
  • 1
    You are loosing `T` this way (forcing `int`), so it changes its meaning. Adding `T` restores issue: https://godbolt.org/z/oYnehxMdb – Marek R Jun 21 '22 at 13:31
  • @MarekR Yes, i've mentioned/added that in my udpated answer. – Jason Jun 21 '22 at 14:37