Actually, things got much easier in C++11 thanks to the decltype
and late return bindings machinery.
Now, it's just simpler to use methods to test this:
// Culled by SFINAE if reserve does not exist or is not accessible
template <typename T>
constexpr auto has_reserve_method(T& t) -> decltype(t.reserve(0), bool()) {
return true;
}
// Used as fallback when SFINAE culls the template method
constexpr bool has_reserve_method(...) { return false; }
You can then use this in a class for example:
template <typename T, bool b>
struct Reserver {
static void apply(T& t, size_t n) { t.reserve(n); }
};
template <typename T>
struct Reserver <T, false> {
static void apply(T& t, size_t n) {}
};
And you use it so:
template <typename T>
bool reserve(T& t, size_t n) {
Reserver<T, has_reserve_method(t)>::apply(t, n);
return has_reserve_method(t);
}
Or you can choose a enable_if
method:
template <typename T>
auto reserve(T& t, size_t n) -> typename std::enable_if<has_reserve_method(t), bool>::type {
t.reserve(n);
return true;
}
template <typename T>
auto reserve(T& t, size_t n) -> typename std::enable_if<not has_reserve_method(t), bool>::type {
return false;
}
Note that this switching things is actually not so easy. In general, it's much easier when just SFINAE exist -- and you just want to enable_if
one method and not provide any fallback:
template <typename T>
auto reserve(T& t, size_t n) -> decltype(t.reserve(n), void()) {
t.reserve(n);
}
If substitution fails, this method is removed from the list of possible overloads.
Note: thanks to the semantics of ,
(the comma operator) you can chain multiple expressions in decltype
and only the last actually decides the type. Handy to check multiple operations.