The biggest reason why we don't have this feature is that it is hard.
It is hard, because it requires compilers be able to compile nearly arbitrary C++ code, get errors, then back out cleanly.
Existing C++ compilers where not all designed to do this. In fact, it took MSVC most of a decade to have reasonably compliant decltype
SFINAE support.
Doing so for full function bodies would be even harder.
Now, even if it was easy, there are reasons not do do this. It mixes implementation and interface in a pretty horrible way.
Rather than go this way, the C++ committee is moving in a completely different direction.
Concepts are the idea that you can express requirements about types in sensible, usually named ways. They are coming in c++20.
As another answer mentions,
template <typename T> requires requires(T t) { { 2 * t } -> T; }
T twice(T t) {
return 2 * t;
}
is a way to do it, but that way is considered bad form. Instead, you should write a concept "can be multiplied by an integer and get the same type back".
template<typename T>
concept IntegerScalable = requires(T t) {
{ 2 * t } -> T;
};
we can then
template <IntegerScalable T>
T twice(T t) {
return 2 * t;
}
and we are done.
A desired next step is called "checked concepts". In checked concepts, the concept it converted into a set of compile-time interfaces for your type T
.
Then the body of the function is checked to ensure nothing is done to anything of type T
that isn't a requirement of a concept.
Using a theoretical future checked concept,
template <IntegerScalable T>
T twice(T t) {
T n = 7;
if (n > t) return n;
return 2 * t;
}
this would be rejected by the compiler when compiling the template even before a call to the template was done, because the concept IntegerScalable
didn't guarantee that you could either initialize a T
with an integer, nor that you could compare one T
to another with >
. Plus I think the above requires move-construction.
There is a hack you can do today.
#define RETURNS(...) \
noexcept(noexcept(__VA_ARGS__)) \
-> decltype(__VA_ARGS__) \
{ return __VA_ARGS__; }
then your code can be written as:
template<class T>
auto twice(T t)
RETURNS( 2 * t )
and you'll get a SFINAE friendly version of twice
. It will also be as noexcept as it can be.
A variant of this using =>
to replace RETURNS
and some other stuff was proposed by @Barry, but it has been a year since I've seen it move.
Meanwhile, RETURNS
does most of the heavy lifting.