23

In C++11 it is legal to write, for instance:

int b = (some_function_returning_void(), 1020);

And you'll get back 1020. But it won't let you write:

int b = (static_assert(2 > 1, "all is lost"), 304);

The documentation explains the legal spots where static_assert (a keyword, apparently) can occur:

A static assert declaration may appear at block scope (as a block declaration) and inside a class body (as a member declaration)

Just for the heck of it I tried a couple things until this worked:

int b = ({static_assert(2 > 1, "all is lost"); 304;});

But with -Wpedantic I get "warning: ISO C++ forbids braced-groups within expressions". Interestingly, these are called "statement expressions" and used in the Linux kernel.

But let's imagine I want to stay -Wpedantic. Are there any clean workarounds?

  • 3
    Context: I'm trying to write a macro that works in ANSI C89, but adds in an extra check if built as ISO C++11. Yes I am weird, thanks for asking. Oh, that wasn't a question? Comment if you're reading this in 99999999999999999.... – HostileFork says dont trust SE Jul 09 '15 at 08:13
  • 1
    You could put the static assert inside a lambda-expression.. `[]{ static_assert(..); }(), 1020` – dyp Jul 09 '15 at 08:14
  • @dyp Interesting idea... though in my note below I ask what the implications of this might be for a macro invoked many, many times... – HostileFork says dont trust SE Jul 09 '15 at 08:35
  • @HostileFork: Macros are just copy-paste, with the penalties implied by that. So: virtually none. As for the lambda call, it's going to be inlined, so again, no penalty. – Mooing Duck Jul 09 '15 at 16:23
  • 1
    Note clang also support [statement expressions](http://stackoverflow.com/a/18885626/1708801) but it is still not portable but clang and gcc covers a lot of the market and clang gives you a way to turn off warnings for them as well `-Wno-gnu-statement-expression` ... it does not seem like gcc does though. – Shafik Yaghmour Jul 10 '15 at 11:58
  • `int b = __extension__ ({static_assert(2 > 1, "all is lost"); 304;});` silences pedantic warnings: "-pedantic and other options cause warnings for many GNU C extensions. You can prevent such warnings within one expression by writing `__extension__` before the expression. `__extension__` has no effect aside from this. " https://gcc.gnu.org/onlinedocs/gcc/Alternate-Keywords.html – ofo Jul 25 '21 at 12:04

3 Answers3

24

As @dyp mentioned in the comments, you can abuse the comma operator and a lambda-expression :

([]{static_assert(true,"");}, 42)

Live on Coliru

Quentin
  • 62,093
  • 7
  • 131
  • 191
  • This definitely works! Though one question I might have is if this macro is very heavily used... like tens of thousands of times, is there a point at which tracking all those lambdas is burdening the compiler? If you do an unoptimized build are you going to have a lot of entities to track which might show up in some debugging scenario? – HostileFork says dont trust SE Jul 09 '15 at 08:34
  • @HostileFork The lambda-expression formally creates a new type (every time it occurs in code) and an object from that new type (every time it is invoked). If that turns out to be a problem, could you maybe use a class template instead? `template struct sassert { static_assert(cond, "!"); static bool const value = false; }; (sassert<(2>1)>::value && "all is lost", 1020)` – dyp Jul 09 '15 at 08:43
  • @dyp Yeah, I was pondering along those lines...since the creation of all the types was my worry) as in not likely an axis the compiler is optimized for compared to the templates. But was wondering if there was something cleaner-looking, and the lambda is indeed cleaner-looking... – HostileFork says dont trust SE Jul 09 '15 at 08:48
  • 7
    @HostileFork even with `-O0`, GCC produces nothing resembling an object ([GodBolt](https://goo.gl/WDT8Ce)). Whether it keeps track of them *during* compilation, I don't know. – Quentin Jul 09 '15 at 08:59
9

static_assert is not an expression (unlike sizeof), so you can't use it where an expression would be required.

It's not even an expression with type void (interestingly, throw is an expression of type void) so you can't even use it in a ternary.

Statement expressions are not standard C++ so I'd advise against using them.

A lambda

int b = []{
    static_assert(2 > 1, "all is lost"); return 304;
}();

or

int b = ([]{static_assert(2 > 1, "all is lost");}, 304);

is hardly clean. (The second lambda looks like a hair's-breadth away from being undefined).

Bathsheba
  • 231,907
  • 34
  • 361
  • 483
  • Nice suggestion with the lambdas, folks. As for the second, why would it be less defined than just `[]{}`? I'd think not calling it would be easier to optimize out... – HostileFork says dont trust SE Jul 09 '15 at 08:28
  • I'm worried about the first thing not being evaluable. The comma operator evaluates all expressions from left to right. – Bathsheba Jul 09 '15 at 08:29
  • It's evaluable to the value of the lambda though, isn't it? `auto x = []{};`? – HostileFork says dont trust SE Jul 09 '15 at 08:30
  • That's my line of thought. Not 100% convinced though; yet. – Bathsheba Jul 09 '15 at 08:30
  • 3
    §5.1.2/2 (N3376): "The evaluation of a lambda-expression results in a prvalue temporary (12.2). This temporary is called the closure object. A lambda-expression shall not appear in an unevaluated operand (Clause 5)." Nothing therein (or the rest of the section, as far as I could check by skimming) suggests that this usage of lambdas was ill-formed. "unevaluated operand" just relates to "sizeof", "decltype" etc. Of course it's impossible to prove a universal negative, which is what it might take to convince you. – Arne Vogel Jul 09 '15 at 10:38
3

What about a function template:

template<int T> void my_static_assert()
{
    static_assert(T, "asserted");
}

Then you can use the comma operator, you don't even need to call the function:

int x = (my_static_assert<(2 > 1)>, 2001);

Maybe you need a few parentheses here and there to make the parser happy. And you loose the static assert message, but it works.

rodrigo
  • 94,151
  • 12
  • 143
  • 190