Whats wrong with this:
#include <type_traits>
struct A;
template<typename T>
struct B
{
template<typename=std::enable_if<std::is_copy_constructible<T>::value>>
void f1() {}
};
template<typename T>
struct C {};
// Type your code here, or load an example.
int main() {
// Following fails
B<A> b;
// Could use this:
// b.f1<C>();
// This complies
C<A> c;
return 0;
}
/* This to be in or not doesn't make a difference
struct A
{};
*/
I tried this here: https://godbolt.org/z/NkL44s with different compilers:
- x86-64 gcc 9.2: compiles
- x86-64 gcc (trunk): fails
- x86-64 clang 6.0.0: compiles
- x86-64 clang 7.0.0 and later: fails
- x64 msvc v19.22: compiles
- x64 msvc v19.23 (tested internally): fails
So why do more recent compilers reject this? When instantiating B<A>
it is not clear in which form f1
will be used or if it will be used at all. So why the compiler complains about it? Shouldn't the f1
member template function be checked only if it is really used?
Edit:
As mentioned in comments, I made an unintented mistake in above code: std::enable_if
should have been std::enable_if_t
, as in this corrected playground: https://godbolt.org/z/cyuB3d
This changes the picture of compilers passing this code without error:
- gcc: fails
- clang: fails
- x64 msvc v19.22: compiles
- x64 msvc v19.23 (tested internally): fails
However, the question remains: Why does a defaulted template parameter of a function that is never used lead to compilation failure?
typename = std::enable_if_t<...>
, nottypename = std::enable_if<...>
. – Jemenatemplate<bool = std::is_copy_constructible_v<T>> void f1() {}
. The problem seems to be related to the time of resolution of default template arguments. Note thatT
being incomplete type results in undefined behaior. The differences I can observe are caused by compiler checks for type completeness. – Greatcoatstd::enable_if
type itself. – Greatcoatstd::enable_if
versusstd::enable_if_t
. – Kabob