[cfe-dev] static if (was: Clang getting involved)
Jonathan 'Rynn' Sauer
jonathan.sauer at gmx.de
Mon Mar 10 01:02:28 PDT 2014
Hello,
> The big problem with 'static if' is that it's three different features that have got muddled together. Those are:
>
> 1) A static if, just like #if, where the condition must be a non-dependent constant expression, and the body can be arbitrary brace-balanced token soup. This basically just allows more advanced constants in #ifs.
>
> constexpr bool has_interval_literals() { return __has_feature(interval_literals); }
> static if (has_interval_literals) {
> static_assert( [1...4) + [4...6] == [1...6], "" );
> }
>
> 2) A template if, that introduces a separately-instantiable template entity (and thus introduces a scope). As is usual for templates, the body must be instantiable for some template arguments (it can't be token soup).
>
> 3) An enable if, that makes declarations visible or invisible depending on some predicate.
>
> Of these:
> - the first isn't hugely useful, since a lot of the "interesting" compile time constants for this sort of check are preprocessor constant expressions too, so #if works,
> - the second is already possible using generic lambdas (but it's a little hacky):
> if_(b, [&](auto){ blah; }, [&](auto){ blah2; }) // implementation left as an exercise to the reader
Maybe I'm misunderstanding something, but when I as a reader tried your exercise, I failed
miserably at first:
#include <type_traits>
template <bool Expr_>
struct StaticIf {
template <typename Code_>
static void apply(Code_)
{
}
};
template <>
struct StaticIf<true> {
template <typename Code_>
static void apply(Code_ code)
{
code(1);
}
};
template <bool Expr_, typename Code_>
void static_if(Code_ code)
{
return StaticIf<Expr_>::apply(code);
}
template <typename T>
void foo(T t)
{
// Call <t.bar> if <T> is a class
static_if<std::is_class<T>::value>([&](auto) {
t.bar();
});
}
struct Bar {
void bar() {}
};
int main()
{
foo(1);
foo(Bar{});
}
This does not compile as <t> inside the generic lambda is not a name dependent on the lambda's
template parameter and thus requires <T> to be a class with a method <bar>.
It is of course possible to make <t> artificially dependent on the lambda's template parameter,
e.g. by saying:
// Make T dependent on D
template <typename T, typename D>
T makeDependent(T t, D /*d*/)
{
return t;
}
template <typename T>
void foo(T t)
{
// Call <t.bar> if <T> is a class
static_if<std::is_class<T>::value>([&](auto dummy) {
auto t2 = makeDependent(t, dummy);
t2.bar();
});
}
This compiles, but IMO is really hacky and inelegant. Similar to local structs are inelegant
compared to lambdas.
Do you know of a more elegant way?
Jonathan
P.S: Correct forwarding of values left as an exercise to the reader ;-)
More information about the cfe-dev
mailing list