带空参数包的一元折叠

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";
}

此代码应输出 1 但会抛出错误:

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

我的问题是: 为什么这行不通,因为 fold_expression 显然在 else 部分。

作为参考,此实现有效:

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;
}

问题是,指定空展开的折叠表达式时,(... * items)编译时无效;即使它不会在 运行 时间进行评估。

你可以使用 constexpr if (C++17 起);那么else部分在被指定为空扩展的折叠表达式时将被丢弃。

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);
    }
}

唯一具有空序列默认值的折叠表达式是&&(默认为true),||(默认为false),和 ,(默认为 void())。

就语言而言,

* 没有默认值。但是在这种情况下,我们可以使用二元折叠而不是一元折叠:

template<std::size_t... items>
constexpr std::size_t fold_mul()
{
    return (1 * ... * items);
}

这适用于空情况(值只是 1)和非空情况(因为 1 是乘法的单位元素,所以不改变值).