12

Suppose I want to write a C++1y/14 constexpr function that performs integer square root:

constexpr int constexpr_isqrt(int x);

I want to perform a sanity check to make sure that x is non-negative:

constexpr int constexpr_isqrt(int x)
{
    if (x < 0)
        ???

    ...
}

What should I write in ??? above?

Ideally if the function is evaluated in constant context it should cause a compile-time error, and if called at run-time with a run-time error (eg abort or thrown exception).

Andrew Tomazos
  • 66,139
  • 40
  • 186
  • 319
  • 2
    possible duplicate of [C++11 - static\_assert within constexpr function?](http://stackoverflow.com/questions/8626055/c11-static-assert-within-constexpr-function) – Mark Garcia Mar 11 '14 at 09:46
  • @MarkGarcia: I am aware of the conditional expression solution but it doesn't apply to my problem. In particular the `...` above consists of multiple statements, so cannot be expressed as a single expression. – Andrew Tomazos Mar 11 '14 at 09:52
  • 1
    I haven't read the new constant expressions relaxations thoroughly, but why should the restriction on `throw` inside `constexpr` functions behave differently from C++11? [Live example](http://coliru.stacked-crooked.com/a/a6a404fb9a6b7ad6) – dyp Mar 11 '14 at 12:39
  • @dyp: Wow, cool. It looks like I can replace `???` with a throw expression, and that is the answer to my question. I thought throw expressions were only allowed in unevaluated contexts. – Andrew Tomazos Mar 11 '14 at 15:11
  • @AndrewTomazos Well, sort of: A throw-expression may not be evaluated inside a constant expression. So, it is unevaluated, but not because the context is per se an unevaluated context. – dyp Mar 11 '14 at 15:13
  • @dyp: I mean _unevaluated operand_ [expr]/8. In your example the throw expression is not an unevaluated operand. – Andrew Tomazos Mar 11 '14 at 16:54
  • @AndrewTomazos Even in C++11, you can have non-constant things like `throw` and `new` appear outside of *unevluated operands*. [expr.const] allows them in *unevaluated sub-expressions* of `&&`, `||` and `?:`. This has been shortened in C++1y by directly referring to the *evaluation* of certain expressions inside the constant expressions (note: not *sub-expressions*, the statement inside `if` is not a sub-expression of the expression in which the function is called, as far as I can tell). – dyp Mar 11 '14 at 17:50

1 Answers1

12

You're in luck, there is a way! Even in C++11! Use exceptions:

#include <iostream>
#include <stdexcept>

constexpr int foo(int a)
{
    return (a >= 0) ? a : throw std::invalid_argument("Negative!");
}

template <int n>
struct Foo
{
};

int main()
{
    Foo<foo(1)> f1();  // fine, guaranteed compile time
    Foo<foo(-1)> f2(); // bad, compile time error
    foo(1);            // fine, not necessarily at compile time
    try
    {
        foo(-1);       // fine, definitively not at compile time
    }
    catch ( ... )
    {
    }
    return 0;
}

Demo: http://ideone.com/EMxe2K

GCC has a rather good error message for the disallowed case:

prog.cpp: In function ‘int main()’:
prog.cpp:17:12:   in constexpr expansion of ‘foo(-1)’
prog.cpp:6:63: error: expression ‘<throw-expression>’ is not a constant-expression
  return (a >= 0) ? a : throw std::invalid_argument("Negative!");

For a C++1y constexpr function that looks like

constexpr foo(int a)
{
    if ( a < 0 )
    {
        // error!
    }
    ++a;
    //something else
    return a;
}

you can use the above pattern by introducing a new function:

constexpr foo_positive(int a)
{
   ++a;
   //something else
   return a;
}

constexpr int foo(int a)
{
   return (a >= 0) ? foo_positive(a) : throw std::invalid_argument("Negative!");
}

Or you simply write

constexpr foo(int a)
{
    if ( a < 0 )
    {
        throw std::invalid_argument("Negative!");
    }
    ++a;
    //something else
    return a;
}
stefan
  • 10,215
  • 4
  • 49
  • 90
  • I don't think it works if i replace `???` with a throw expression in my OP. The technique you describe only works if the rest of the function can be expressed as an expression. – Andrew Tomazos Mar 11 '14 at 09:50
  • @AndrewTomazos it always can be: Just put the stuff for positive input in a separate function. – stefan Mar 11 '14 at 09:59
  • @AndrewTomazos See my updated answer for an application of this pattern to a C++1y constexpr function. – stefan Mar 11 '14 at 10:10
  • You don't need to introduce a new function in your C++1y version; you can just use `if`. – Richard Smith Mar 22 '14 at 22:25
  • @RichardSmith: it's about having the same pattern. Sure, an if will work just as well – stefan Mar 22 '14 at 22:40
  • 1
    That pattern is a workaround for a language deficiency that C++1y doesn't have. – Richard Smith Mar 23 '14 at 17:22
  • @RichardSmith That doesn't really make it bad, since it's useable right now _and_ in the future as well. But I've added the C++1y version as well now. – stefan Mar 23 '14 at 18:05