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.
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
}
Note also that if you use NodeMaker<T>::create_node(0);
from inside do_something
then the issue will reappear.