Versions Compared

Key

  • This line was added.
  • This line was removed.
  • Formatting was changed.
Comment: minor editorial changes

Some expressions involve operands that are unevaluated. According to the The C++ Standard, [expr], paragraph 8 [ISO/IEC 14882-2014] states the following:

In some contexts, unevaluated operands appear. An unevaluated operand is not evaluated. An unevaluated operand is considered a full-expression. [Note: In an unevaluated operand, a non-static class member may be named (5.1) and naming of objects or functions does not, by itself, require that a definition be provided. — end note]

The following expressions do not evaluate their operands: sizeof()typeid()noexcept()decltype(), and declval().

Because an unevaluated operand in an expression is not evaluated, no side effects from that operand are triggered. Reliance on those side effects will result in unexpected behavior. Do not rely on side effects in unevaluated operands.

Note that unevaluated Unevaluated expression operands are used when the declaration of an object is required but the definition of the object is not. For instance, in the following example, the function f() is overloaded, relying on the unevaluated expression operand to select the desired overload, which is then used to determine the result of the sizeof() expression:.

Code Block
int f(int);
double f(double);
size_t size = sizeof(f(0));

...

In this noncompliant code example, the expression a++ is not evaluated:.

Code Block
bgColor#FFCCCC
langcpp
#include <iostream>
void f() {
  int a = 14;
  int b = sizeof(a++);
  std::cout << a << ", " << b << std::endl;
}

...

In this compliant solution, the variable a is incremented outside of the sizeof operator:.

Code Block
bgColor#ccccff
langcpp
#include <iostream>
void f() {
  int a = 14;
  int b = sizeof(a);
  ++a;
  std::cout << a << ", " << b << std::endl;
}

...

In this noncompliant code example, the expression i++ is not evaluated within the decltype specifier:.

Code Block
bgColor#FFCCCC
langcpp
#include <iostream>

void f() {
  int i = 0;
  decltype(i++) h = 12;
  std::cout << i;
}

...

In this compliant solution, i is incremented outside of the decltype specifier so that it is evaluated as desired:.

Code Block
bgColor#ccccff
langcpp
#include <iostream>

void f() {
  int i = 0;
  decltype(i) h = 12;
  ++i;
  std::cout << i;
}

...

The following code is an example of compliant code using an unevaluated operand in a macro definition:.

Code Block
bgColor#ccccff
langcpp
void small(int x);
void large(long long x);
 
#define m(x)                                     \
  do {                                           \
    if (sizeof(x) == sizeof(int)) {              \
      small(x);                                  \
    } else if (sizeof(x) == sizeof(long long)) { \
      large(x);                                  \
    }                                            \
  } while (0)
 
void f() {
  int i = 0;
  m(++i);
}

The expansion of the macro m will result in the expression ++i being used as an unevaluated operand to sizeof(). However, the expectation of the programmer at the expansion loci is that i is preincremented only once. Consequently, this is a safe macro , and complies with PRE31-C. Avoid side effects in arguments to unsafe macros. Compliance with that rule is especially important for code that follows this exception.

The following code is an example of compliant code using an unevaluated operand in a SFINAE context to determine whether a type can be postfix incremented:.

Code Block
bgColor#ccccff
langcpp
#include <iostream>
#include <type_traits>
#include <utility>

template <typename T>
class is_incrementable {
  typedef char one[1];
  typedef char two[2];
  static one &is_incrementable_helper(decltype(std::declval<typename std::remove_cv<T>::type&>()++) *p);
  static two &is_incrementable_helper(...);
  
public:
  static const bool value = sizeof(is_incrementable_helper(nullptr)) == sizeof(one);
};

void f() {
  std::cout << std::boolalpha << is_incrementable<int>::value;
}

...