I have been having some inexplicable SFINAE problems in a program I’m writing, so I boiled it down to a freestanding example program:
#include <type_traits>
struct Base { };
struct Derived : public Base { };
template<typename T>
struct From { };
template<typename T>
struct To {
template<typename U>
To(const From<U>& other) {
static_assert(std::is_convertible<U*, T*>::value, "error");
}
};
int main() {
From<Derived> a;
To<Base> b = a;
}
This program compiles without error or warning. However, this:
#include <type_traits>
struct Base { };
struct Derived : public Base { };
template<typename T>
struct From { };
template<typename T>
struct To {
template<typename U>
To(const From<typename std::enable_if<true, U>::type>& other) {
// this ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
static_assert(std::is_convertible<U*, T*>::value, "error");
}
};
int main() {
From<Derived> a;
To<Base> b = a;
}
Gives the following error:
test.cpp: In function
int main():test.cpp:22:18: error: conversion from
From<Base>to non-scalar typeTo<Derived>requested
Which is because the substitution fails I presume, and the constructor isn’t seen.
Am I doing SFINAE wrong, or is this a compiler bug? I am using rubenvb’s GCC 4.7.1 on Windows (with std=c++11 if it makes a difference).
I’d use a default template argument, that way the argument can be deduced:
Note that that causes the
static_assertto fail since you are usingstd::is_convertiblethe other way around. It should be:In your example, the template type
Ucan’t be deduced. In my code, it can be deduced, since it is being used as a template argument for theotherargument in the constructor. In your code, the compiler sees astd::enable_if<true, U>::typeand can’t deduce what thatUtype is. The fact that the result of thatenable_ifis being used as a template argument forFromdoesn’t help at all, sinceUneeds to be deduced before that.