
On Oct 16, 2007, at 5:58 AM, Marco Costalba wrote:
Starting to hack with boost libraries I found myself more and more stumbling across a big brick of C++ language, i.e. SFINAE works on types only, not on expressions.
If an expression is ill formed there's no SFINAE around that will avoid you a barfing compiler.
This issue has been actively discussed in the C++ committee. You can track it's progress via core issue 339 on the Core Issues list, which is here: http://www.open-std.org/jtc1/sc22/wg21/docs/cwg_active.html#339 At the very bottom of the issue text, there is a note that describes the latest view on the matter. The resolution we're likely to get is that SFINAE will be extended to support expressions. There are still cases that will cause "hard" failures (not SFINAE failures), for example if one ends up trying to instantiate a class template inside a sizeof() expression and that instantiation fails.
Looking at C++0x I failed to see a future improvement in that direction, also 'concepts' that will be quite new, apart from helping in decoding error messages (and this is a BIG help I would add), does not immediately seem to extend the language in that direction.
Concepts are actually quite a big improvement in this area. Mathias Gaunard mentioned how the use of concepts along with (concept-based) overloading can solve the kind of problem you're describing, where you need to query the properties of a type and decide between two alternatives. Concepts allow you to describe the properties of types via a set of requirements. For example, here's a short concept that describes some properties of numeric types: concept Numeric<typename T> { T::T(T const &); T operator+(T, T); T operator-(T, T); T operator*(T, T); T operator/(T, T); T operator-(T); T operator+(T); } Many types meet the requirements of this concept: int, float, double, and a user-defined bigint, for example, would all have the necessary copy constructor and arithmetic operations. Using concepts, we can write generic algorithms as "constrained" templates, e.g., template<typename T> requires Numeric<T> T twice(T x) { return x + x; } Now, for integral types (int, long, etc.), we would really rather implement twice() with a simple shift operation. So, we first define a new concept that captures the notion of an integral type: concept Integral<typename T> : Numeric< T> { T operator<<(T, int); T operator>>(T, int); } Integral is a *refinement* of Numeric, meaning that it inherits all of the requirements of the Numeric concept. So, to be an integral type on needs to have all of the operations from Numeric (+, -, *, /, etc.) and from Integral (<<, >>). Every type that is Integral is also Numeric. Using the Integral concept, we can write an overload of the twice() function: template<typename T> requires Integral<T> T twice(T x) { return x << 1; } Now, when a user calls twice() with a particular type, the compiler will find both overloads. If the type is Numeric, the first overload works and will be chosen; if the type is Integral, both overloads work but the second (more specific, more efficient) overload will be chosen. So instead of some kind of "try_compile" block, you just write down the requirements you need to check (e.g., whether the type is integral, whether it has a << operator, etc.) as a concept, and then write two overloads: one if that property is satisfied, one if that property is not satisfied. - Doug