Search code examples
c++lambdac++17static-casttrailing-return-type

Lambda expression in C++17: trailing return type vs static_cast for type conversion


How to convert int to long inside/outside of lambda expression properly? How to check overflow of mathematics inside lambda properly?

int n = 12; // input parameter from std::cin
int a = 23; // input parameter from std::cin
int i = 34; // input parameter from std::cin
auto f = [n, a] (int i) { return a * (n - (i - 1)); };
auto result = f(i);

What's the best way to check for overflow after multiplying of integers inside/outside of lambda?

auto result = f(i);
if ((result > std::numeric_limits<int>::max()) || (result < std::numeric_limits<int>::min())) {
    cout << "overflow was detected" << endl;
}

Do I need to add TRT (trailing return type) -> long to lambda for proper conversion from int to long?

auto f = [n, a] (int i) -> long { return a * (n - (i - 1)); };

Do I need to add static_cast to lambda for proper conversion from int to long:

auto f = [n, a] (int i) { return static_cast<long>(a) * (n - (i - 1)); };

Or, may be, I need to combine this?

auto f = [n, a] (int i) -> long { return static_cast<long>(a) * (n - (i - 1)); };

Or, may be, I need to write the type of lambda?

std::function< long( int ) > f = [n, a] (int i) { return a * (n - (i - 1)); };

Solution

  • First part of your question is a duplicate (although C question, C++ doesn't differ, and there are duplicates of including C++). Most important point: You cannot detect signed integer overflow after multiplication, as it already caused undefined behaviour – unless you do calculation in next larger data type; be aware, though, that int and long on many (but not all!) platforms (including Windows on modern PC hardware) have the same size, so switching to long is not a guarantee to avoid overflow! If you want to be safe, use the data types from <cstdint> header (such as int16_t, int32_t, int64_t). If you have some specific reason to insist on int, you'd have to invest some extra work to get guaranteed next larger type. This question is not specific to lambdas, though, but concerns any multiplication (and even addition).

    To guarantee a specific return type (again, the question is not specific to lambdas, but to any automatic deduction of type, be it a return type or an auto variable), both aproaches are valuable (explicit trailing return type and a cast), however, as presented, the results do differ!

    Just applying the trailing return type is equivalent to having

    [n, a] (int i) { return static_cast<long>(a * (n - (i - 1))); };
    //                                         ^               ^
    

    Note that the cast is placed around the entire calculation, i. e. the cast is done after the calculation, which still is done in int. If size of int and long do differ, the result can differ from your own casting variant:

    [n, a] (int i) { return static_cast<long>(a) * (n - (i - 1)); };
    

    as this enforces already the multiplication being done in long (but not the subtractions! – to enforce even these, too, you'd need to cast i). This cast is entirely sufficient to get the desired return type; an explicit trailing return type (in addition) shows more clearly, though, what the lambda actually returns without having to peek into the implementation. This is especially useful if you have a more complex lambda, and it can help to guarantee a consistent return type if you have multiple exit points.