#include <cassert>
#include <cmath>
#include <cstdint>

enum class ECode : uint64_t {
    OK = 0xFFF0'0000'0000'0001,
    ERROR,
    INPUT_IS_NAN,
    INPUT_IS_INFINITE,
    INPUT_IS_NEGATIVE,
};

union Result_or_code
{
    double result;
    ECode code;
    Result_or_code(double x) {result = x;}
    Result_or_code(ECode c) {code = c;}
    operator double() {return result;}
    operator ECode() {return code;}
};

Result_or_code sqrt_or_not(double x) {
    if (std::isnan(x))
        return ECode::INPUT_IS_NAN;
    if (std::isinf(x))
        return ECode::INPUT_IS_INFINITE;
    if(x < 0)
        return ECode::INPUT_IS_NEGATIVE;
    return std::sqrt(x);
}

int main(void) {
    assert(sqrt_or_not(-1.) == ECode::INPUT_IS_NEGATIVE);
    assert(sqrt_or_not(1./0.) == ECode::INPUT_IS_INFINITE);
    assert(sqrt_or_not(0./0.) == ECode::INPUT_IS_NAN);
    assert(sqrt_or_not(4.) == 2.);

    assert(std::isnan(sqrt_or_not(-1.)));
    assert(std::isnan(sqrt_or_not(1./0.)));
    assert(std::isnan(sqrt_or_not(0./0.)));
    assert(std::isnan(sqrt_or_not(-1.) + 1.));
}
	

...

I've made this simple benchmark to test how noticeable the gains really are.

#define MEASURE(CODE_TO_MEASURE) \
    { \
    auto start = std::chrono::system_clock::now(); \
    CODE_TO_MEASURE \
    auto end = std::chrono::system_clock::now(); \
    std::chrono::duration<double> difference = end - start; \
    std::cout << difference.count(); \
    }
	

Here are eight snippets of code. The first three have error codes as NaNs, then there are a pair of snippets for boost::optional, then there are two more for tuples, and, last but not least, an exception throwing.

Checking error codes as enum
Result_or_code sqrt_or_not(double x) { if (std::isnan(x)) return ECode::INPUT_IS_NAN; if (std::isinf(x)) return ECode::INPUT_IS_INFINITE; if(x < 0.) return ECode::INPUT_IS_NEGATIVE; return std::sqrt(x); } ... MEASURE( for(double x = -1024.; x <= 1024.; x += 1./65536.) { auto root = sqrt_or_not(x); if(root >= ECode::ERROR) ++errors; else { ++results; total += root; } } );
Time: 0.2 s Return type size: 8 B

Measured on Intel(R) Core(TM) i7-7700HQ CPU @ 2.80GHz
Compiled with clang version 3.8.0-2ubuntu4
Benchmark code is available on Github.