1

This is what I would like to do:

void g() {
    std::cout << "smth1" << std::endl;
    std::cout << "smth2" << std::endl;
}

constexpr bool f() {return g(), true;}

The compiler (gcc version 4.8.2) is not happy with this, since g is not constexpr. Is it possible to get around this problem?

AlwaysLearning
  • 7,257
  • 4
  • 33
  • 68
  • 9
    What would *you* expect from calling a function with some side effect (producing output in this case) during a compile-time evaluation? – 5gon12eder Dec 09 '15 at 17:37
  • @5gon12eder When I write `if (f()) {...}`, I would like the compiler to get rid of that branch during the optimization phase, since f() is guaranteed at compile-time to return `true`. – AlwaysLearning Dec 09 '15 at 17:39
  • @AlwaysLearning So you want the compiler to execute the side effects of `g` but eliminate the branch? – Mark B Dec 09 '15 at 17:40
  • @interjay True, but this restriction was lifted in C++14 while the conceptual problem remains. – 5gon12eder Dec 09 '15 at 17:41
  • @MarkB What's wrong with that? Isn't it known at compile-time that f() returns `true`? So, if the client writes `if (!f())`, can't the compiler know that the condition will not be satisfied? – AlwaysLearning Dec 09 '15 at 17:44
  • @5gon12eder My comment refers to the fact that the function had two different return types specified. – interjay Dec 09 '15 at 17:44
  • @5gon12eder The function referred to in the title refers to `g`, which does return `void`. – AlwaysLearning Dec 09 '15 at 17:46
  • Note, you may get [gcc to accept non-constexpr function](http://stackoverflow.com/q/22176777/1708801) that have builtins but it is still ill-formed and not portable. – Shafik Yaghmour Dec 09 '15 at 18:09

2 Answers2

4

The point of constexpr is that it can be fully expanded at compile-time. There is no possible way that the compiler can put runtime side effects (using cout) into what is effectively a more complex compile-time constant. Put another way: No actual function call will be made at runtime for constexpr functions!

Luckily the solution is easy!

Change f to constexpr bool f() { return true;}

and your conditional to:

g();
if(f())
{
    // ...
}
Mark B
  • 95,107
  • 10
  • 109
  • 188
  • In my case, `g` is a template argument. I wanted to allow `g` to return either `void` or `bool`. I tag-dispatch on the return type of `g`. The code I showed is for the case when `g` returns `void`. When `g` returns `bool`, I return `g()`, which is then used in a conditional statement. I wanted to avoid branching in the case when `g` returns `void`. – AlwaysLearning Dec 09 '15 at 17:52
  • The reply does answer the question as it was asked, so accepting it. I will ask another question that will provide all the relevant details. – AlwaysLearning Dec 09 '15 at 18:03
1

Any evaluation of a function called during the evaluation of a constant expression must not have any side effects. In C++11, the rules are actually much stricter, allowing only a single return statement as function body but that's not relevant to this question.

The compiler is not entitled to remove any code from a constexpr function in order to make it comply. It is your responsibility to not write such code in the first place.

However, it is okay to have code with side effects inside a constexpr function if the control flow during static evaluation never passes through it.

This is valid C++11.

void g() { std::cout << "hello, world" << std::endl; }
constexpr bool f(const bool p = false) { return p ? (g(), false) : true; }

And you may call f like this.

constexpr auto x = f();  // ok

Because p is false at compile-time, the compiler need not evaluate the call to g. At run-time, you may call f with either argument just as if it were not a constexpr function.

f(false);  // ok
f(true);   // ok, produces output at run-time 

What you cannot do is evaluate it in a constant expression with the parameter set to true.

constexpr auto x = f(true);  // compile-time error

Of course, this example is contrived beyond any restrictions and you should simply write

constexpr bool f() noexcept { return true; }

or use a variable

constexpr auto toggle = true;

if this is all you need.

5gon12eder
  • 24,280
  • 5
  • 45
  • 92