Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Unary fold with empty parameter pack

#include <iostream>
#include <utility>

template<std::size_t... items>
constexpr std::size_t count()
{
    return std::index_sequence<items...>().size();
}

template<std::size_t... items>
constexpr std::size_t fold_mul()
{
    if( count<items...>() == 0 )
    {
        return 1;
    }
    else
    {
        return (... * items);
    }
}

int main()
{
    std::cout << "Result: " << fold_mul<>() << "\n";
}

This code is expected to output 1 but instead throws the error:

<source>:19:28: error: fold of empty expansion over operator*
19 | return (... * items);

My question is: Why doesn't this work, as the fold_expression is clearly in the else part.

For reference, this implementation works:

template<typename... Args>
constexpr std::size_t fold_mul();

template<std::size_t... j>
requires (count<j...>() > 0)
constexpr std::size_t fold_mul()
{         
    return (j * ...);
}

template<>
constexpr std::size_t fold_mul()
{
    return 1;
}
like image 583
infinitezero Avatar asked Dec 14 '22 07:12

infinitezero


1 Answers

The problem is, when being specified fold expression with empty expansion, (... * items) is invalid at compile-time; even it won't be evaluated at run-time.

You can use constexpr if (since C++17); then the else-part will be discarded when being specified fold expression with empty expansion.

If the value is true, then statement-false is discarded (if present), otherwise, statement-true is discarded.

template<std::size_t... items>
constexpr std::size_t count()
{
    return std::index_sequence<items...>().size();
}

template<std::size_t... items>
constexpr std::size_t fold_mul()
{
    if constexpr ( count<items...>() == 0 )
    // ^^^^^^^^^
    {
        return 1;
    }
    else
    {
        return (... * items);
    }
}
like image 178
songyuanyao Avatar answered Dec 30 '22 17:12

songyuanyao