[Bug c++/86689] New: Some combination of SFINAE, overloading, and type deduction showing version inconsistency

bugs at grumpyplatypus dot com gcc-bugzilla@gcc.gnu.org
Thu Jul 26 18:25:00 GMT 2018


https://gcc.gnu.org/bugzilla/show_bug.cgi?id=86689

            Bug ID: 86689
           Summary: Some combination of SFINAE, overloading, and type
                    deduction showing version inconsistency
           Product: gcc
           Version: 8.2.0
            Status: UNCONFIRMED
          Severity: normal
          Priority: P3
         Component: c++
          Assignee: unassigned at gcc dot gnu.org
          Reporter: bugs at grumpyplatypus dot com
  Target Milestone: ---

The following code is accepted by GCC 4.7.3-7.3.0, but 8.1.0 and 8.2.0 do not
accept it:

// Fail.cpp
template <typename T> struct SomeClassTemplate {};

struct TrueType { static constexpr bool value = true; };
struct FalseType { static constexpr bool value = false; };

template <bool B, typename T=void> struct EnableIfT {};
template <typename T> struct EnableIfT<true,T> { using type = T; };

template <typename T> struct IsAType : TrueType {};

template <typename Pred,typename T=void>
using EnableIf = typename EnableIfT<Pred::value,T>::type;

template <typename T, typename=EnableIf<IsAType<T>,int>>
int SomeFunc(T const&) { return 1; }

template <typename T, typename=EnableIf<IsAType<T>,int>>
int SomeFunc(SomeClassTemplate<T> const&) { return -1; }

template int SomeFunc(double const&);
template int SomeFunc(SomeClassTemplate<double> const&);

int main()
{
  double x;
  SomeClassTemplate<double> y;

  auto a = SomeFunc(x);
  auto b = SomeFunc(y);
  return a + b;
}
// End Fail.cpp

Sorry about all manual EnableIf and whatnot -- I wanted #include-less code.
Also, the predicate is dumb, but meant to match the case in "real code".

The compiler error is:

> g++ Fail.cpp
Fail.cpp:22:14: error: ambiguous template specialization ‘SomeFunc<>’ for ‘int
SomeFunc(const SomeClassTemplate<double>&)’
 template int SomeFunc(SomeClassTemplate<double> const&);
              ^~~~~~~~
Fail.cpp:16:5: note: candidates are: ‘template<class T, class> int
SomeFunc(const T&)’
 int SomeFunc(T const&) { return 1; }
     ^~~~~~~~
Fail.cpp:19:5: note:                 ‘template<class T, class> int
SomeFunc(const SomeClassTemplate<T>&)’
 int SomeFunc(SomeClassTemplate<T> const&) { return -1; }


I am especially confused since commenting out the explicit instantiation of
this function allows compilation to succeed, in particular the implicit
instantiation in main() is successful and chooses the correct overload.


Some other details:

> g++ -v
Using built-in specs.
COLLECT_GCC=g++
COLLECT_LTO_WRAPPER=/path/to/gcc/8.1.0/libexec/gcc/x86_64-pc-linux-gnu/8.1.0/lto-wrapper
Target: x86_64-pc-linux-gnu
Configured with: ./configure --prefix=/path/to/gcc/8.1.0
--enable-languages=c,c++,fortran,objc,obj-c++,lto --disable-multilib
Thread model: posix
gcc version 8.1.0 (GCC)

> g++ -v
Using built-in specs.
COLLECT_GCC=g++
COLLECT_LTO_WRAPPER=/path/to/gcc/8.2.0/libexec/gcc/x86_64-pc-linux-gnu/8.2.0/lto-wrapper
Target: x86_64-pc-linux-gnu
Configured with: ./configure --prefix=/path/to/gcc/8.2.0
--enable-languages=c,c++,fortran,objc,obj-c++,lto --disable-multilib
Thread model: posix
gcc version 8.2.0 (GCC)

This code is accepted, with at least -std=c++11, by GCC 4.7.3-7.3.0, the latest
clang, both release and git versions, as well as Intel 18.0.2 and 19.0-beta,
PGI 18.5, and XL.

Also, the following code compiles without issue with 8.1.0 and 8.2.0:

// Ok.cpp
template <typename T> struct SomeClassTemplate {};

struct TrueType { static constexpr bool value = true; };
struct FalseType { static constexpr bool value = false; };

template <bool B, typename T=void> struct EnableIfT {};
template <typename T> struct EnableIfT<true,T> { using type = T; };

template <typename T> struct IsAType : TrueType {};

template <typename Pred,typename T=void>
using EnableIf = typename EnableIfT<Pred::value,T>::type;

template <typename T, EnableIf<IsAType<T>>* = nullptr>
int SomeFunc(T const&) { return 1; }

template <typename T, EnableIf<IsAType<T>>* = nullptr>
int SomeFunc(SomeClassTemplate<T> const&) { return -1; }

template int SomeFunc(double const&);
template int SomeFunc(SomeClassTemplate<double> const&);

int main()
{
  double x;
  SomeClassTemplate<double> y;

  auto a = SomeFunc(x);
  auto b = SomeFunc(y);
  return a + b;
}
// End ok.cpp

The only difference between this and Fail.cpp is in EnableIf style.

> g++ Ok.cpp
>

I'm concerned that it accepts Ok.cpp but not Fail.cpp (or that it does not
accept Fail.cpp and does accept Ok.cpp), but I don't know the standard well
enough to say 100% that one or the other or both is right or wrong.

Anyway, this issue breaks a bunch of code that I didn't write but that I have
to use, so any clarification would be most appreciated.


More information about the Gcc-bugs mailing list