Reputation: 75130
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).
Upvotes: 1
Views: 827
Reputation: 21900
I'd use a default template argument, that way the argument can be deduced:
#include <type_traits>
struct Base { };
struct Derived : public Base { };
template<typename T>
struct From { };
template<typename T>
struct To {
template<typename U, class = typename std::enable_if<true, U>::type>
To(const From<U>& other) {
// this ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
static_assert(std::is_convertible<U*, T*>::value, "error");
}
};
int main() {
From<Base> a;
To<Derived> b = a;
}
Note that that causes the static_assert
to fail since you are using std::is_convertible
the other way around. It should be:
static_assert(std::is_convertible<T*, U*>::value, "error");
In your example, the template type U
can't be deduced. In my code, it can be deduced, since it is being used as a template argument for the other
argument in the constructor. In your code, the compiler sees a std::enable_if<true, U>::type
and can't deduce what that U
type is. The fact that the result of that enable_if
is being used as a template argument for From
doesn't help at all, since U
needs to be deduced before that.
Upvotes: 3