[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