Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Problem in GCC/C++17 with template template class

Consider the 2 following overloads

template<typename T>
bool test() {
    return true;
}

template<template<typename ...> class T>
bool test() {
    return false;
}

The 1st one works for regular classes, while the 2nd one works for templates that are not instantiated. For instance:

    std::cout<<test<int>()<<std::endl; <-- this yields 1
    std::cout<<test<std::list>()<<std::endl; <--this yields 0

Now consider the following template function:

template<typename U>
bool templfun(){
    struct A{
        bool f(){
            return test<A>(); // <-- this gives an error
        }
    };
    return test<A>();  // <-- this is ok
}

In GCC it gives an error for ambiguous overload resolution, while Clang compiles. Interestingly, the second call to test() doesn't produce errors (even in GCC). Moreover, if I remove the template<typename U> thing on top of templfun, gcc stops complaining.

Is this a bug with GCC or is it illegal code?

like image 918
Mario Demontis Avatar asked Feb 18 '20 10:02

Mario Demontis


1 Answers

GCC is wrong; struct A is a templated entity but clearly not a template (as it does not start with a template keyword), so there is no ambiguity.

To confirm, we can rename the type parameter to see that G++ is attempting to use the template-template overload.

template <typename X>
bool test() {
    return true;
}

template <template <typename...> class Y>
bool test() {
    return false;
}

template <typename U>
bool templfun() {
    struct A {
        bool f() {
            return test<A>(); // <-- this gives an error
        }
    };
    return test<A>(); // <-- this is ok
}

bool run() {
    return templfun<int>();
}

G++ output: (link to godbolt)

<source>:15:27: error: call of overloaded 'test<templfun() [with U = int]::A>()' is ambiguous
   15 |             return test<A>(); // <-- this gives an error
      |                    ~~~~~~~^~

<source>:2:6: note: candidate: 'bool test() [with X = templfun() [with U = int]::A]'
    2 | bool test() {
      |      ^~~~

<source>:7:6: note: candidate: 'bool test() [with Y = templfun()::A]'
    7 | bool test() {
      |      ^~~~

Clearly "candidate: 'bool test() [with Y = templfun()::A]'" is bogus.

Note that local types were not allowed as template arguments prior to C++11 (see C++03 § 14.3.1.2), so that could explain the complexity of the G++ implementation.

like image 92
rustyx Avatar answered Oct 19 '22 22:10

rustyx