Potentially-evaluated expressions

An expression is potentially evaluated unless
it is the operand of the sizeof, noexcept, typeid, decltype etc..

https://en.cppreference.com/w/cpp/language/expressions#Potentially-evaluated_expressions

Also

The noexcept operator performs a compile-time check that returns true if an expression is declared to not throw any exceptions

The noexcept operator does not evaluate expression

https://en.cppreference.com/w/cpp/language/noexcept

I'm having difficulties grasping what does it mean to be non-evaluated expression?
For example, if the compiler does compile time check if an expression evaluates to true then how is that expression NOT evaluated?

What does it really mean to be not evaluated?

Another example:

sizeof (a + b)

https://en.cppreference.com/w/cpp/language/sizeof

sizeof does not evaluate therefore a + b, so how does it then know the size of a + b if it does not evalueate it?

Bottom line, what is evaluated expression vs non-evaluated one? and how do these operators return result without evaluating their operands?
Last edited on
I would think it is about the difference of runtime and compile time evaluation.

Things like sizeof (a + b) can be evaluated at compile time.
Everything that may be changed at runtime cannot be evaluated at compile time.
Everything that may be changed at runtime cannot be evaluated at compile time

Thus if sizeof(/* runtime expression */) should result in compile time error because sizeof doesn't evaluate expressions, but we know this is not the case.

Is it valid to say that sizeof(/* runtime expression */) results in sizeof of the result of an expression that is evaluated at runtime but not evaluated by the sizeof operator itself?
1
2
3
4
5
6
7
8
9
10
11
12
13
14
#include <iostream>

int i = 99 ;

// *** warning: expression with side effects has no effect in an unevaluated context
void foo() noexcept( noexcept( sizeof(++i) == 0 ) ) { std::cout << "foo is called\n" ; }

int main()
{
    // *** warning: expression with side effects has no effect in an unevaluated context
    std::cout << std::boolalpha << ( sizeof(++i) > 0 ) << ' ' << noexcept( foo() ) << '\n' ; 

    std::cout << i << '\n' ; // 99 (++i is in unevalated expressions)
}

http://coliru.stacked-crooked.com/a/4ff6981e15ff3886
JLBorges,

This is very good example, I think I get it now.
One issue though, I run MS static analysis which says this:

warning C26447: The function is declared 'noexcept' but calls function 'operator<<<std::char_traits<char> >()' which may throw exceptions (f.6)


Which indicates that foo() is noexcept,
but foo() is NOT noexcept right?

So what is going on with this part?

edit:
noexcept( noexcept( sizeof(++i) == 0 ) ) should evaluate to noexcept(false), so why is it then evaluated to noexcept(true)
Last edited on
According to the notes linked, the noexcept expression returns true if the expression inside does not throw. (sizeof(++i) == 0) does not throw.
Ah indeed, void foo() noexcept(sizeof(++i) == 0) is a different story.

Thank you guys, I absolutely get it now, very good example!
To evaluate an expression essentially means that the expression code is executed in order to produce a value.

sizeof just looks at the type of the expression to determine the size. It doesn't need to evaluate the expression because the value is irrelevant.
Last edited on
I see:

1
2
3
4
5
6
7
8
9
#include <iostream>

short a = 0;
long b = 1;

int main()
{
	std::cout << sizeof(a + b) << std::endl;
}


returns sizeof(long) but does not add a + b,
a + b could be either compile time or run time expression, so I guess sizeof only takes static type of variables somehow and determines resulting type.

What I still don't understand is at which point it does so?
is it compile time or run time?
Last edited on
Yes, you have understood correctly.

The compiler determines the size at compile time.
So of course the compiler looks at the expression to see that it doesn't have any syntax errors and to determine the type, etc. I guess this is a form of "evaluation" using the normal English meaning of the word but in programming "evaluate" has a special meaning so in order to avoid confusion it's best to use different words to describe this.

https://en.wiktionary.org/wiki/evaluate#Verb
Last edited on
yes, the language is missing those terms, all kinds of expressions are just called expressions.
What terms is it missing?
Last edited on
ex. term of the goal of some expression evaluation,
is it to determine the result, to determine type or size or something else.

Just saying to evaluate sounds like deriving a result of computation.
Last edited on
sizeof is evaluated at compile-time (based on the static type of the expression).
(Note: the result of sizeof is a constant expression)

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
#include <iostream>

struct base { char c[5] {} ; virtual std::size_t size() const noexcept { return sizeof(*this) ; } };

struct derived : base { char c[29] {} ; virtual std::size_t size() const noexcept override { return sizeof(*this) ; } };

void foo( const base& b )
{
    std::cout << "size (compile-time): " << sizeof(b) << '\n' // sizeof(base)
              << "size (run-time)    : " << b.size() << '\n' ; // execute final overrider of the virtual function
}

int main()
{
    const derived d ;
    foo(d) ;
}

http://coliru.stacked-crooked.com/a/6a48f2519f2352ab
Just to be clear, I didn't mean we should avoid the word "evaluate" when talking about finding out the value (and possibly getting the side-effects) of an expression. This is well-established vocabulary in our field. What we should not do is use "evaluate" to mean other things, at least not when talking about things related to expressions, because that would only lead to confusion.
Last edited on
I see, this answers all my questions.
thank you for sample code.
In sizeof(EXP),
EXP (the expression which forms the operand to sizeof) is unevaluated.
sizeof(EXP) may be evaluated at compile time.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
#include <iostream>

int i = 99 ;

double foo() { ++i ; if( i > 9 ) throw 0 ; return 0 ; } // throws if foo() is evaluated

int main()
{
    // the subexpression foo() * ++i is unevaluated
    // value of sz - sizeof(double) - is evaluated at compile-time
    constexpr std::size_t sz = sizeof( foo() * ++i ) ;
    static_assert( sz == sizeof(double) ) ;
    std::cout << sz << '\n' ;
}

http://coliru.stacked-crooked.com/a/8df73849185b93d3
static_assert is a good trick to see if it's evaluated at compile time.
Thank you again for sample, or better to say, a prof.
malibor wrote:
static_assert is a good trick to see if it's evaluated at compile time.

It's a good trick to see if an expression can be evaluated at compile time, but it doesn't necessarily mean the same expression will be evaluated at compile time when used in another context.
Topic archived. No new replies allowed.