Hello Barry

Thank you for the hint to the other proposals.

To the first example with "template for (constexpr": This is interesting since it can replace all usages of std::make_index_sequence which usage in general requires a second function definition.However, I cannot find this kind of syntax "template for (constexpr" in https://wg21.link/P1306.

To the second example with "for ...": With this construct you can in principle replace a lot of other fold expression if you know the combined type generated. But that does not mean that you do not want to have those fold expressions, e.g.

    template <typename... Values>
    auto add(Values... vs)
    {
        return (... + vs);
    }

vs.

    template <typename... Values>
    auto add(Values... vs)
    {
         auto ret = std::common_type_t<Values...>{};
         for...{
             ret += vs;
         }
         return ret;
    }

The question of the combined type is still the question if you have an expression like

auto result = (conditions ? values : ... : std::unreachable());

where the values have different types. What should be the return type for the function covering the for...?

    for...{
         if (conditions) {
            return values;
        }
    }

With a classical fold expression, you do not have to worry about the combined type, since the compiler figures it out for you.

By the way, I cannot find the syntax "for...{}" in https://wg21.link/P1306, too. There is ony the little bit longer syntax of "for...(){}".With this longer syntax I am not really sure how to write a combined for loop over "translators::language" and "translators::translate_to_english".As far as I understand P1306 you cannot directly iterate over the types "translators" and the flavor you revered to a suggestion of Richard Smith or Andrew Sutton seems not to be formalized in a proposal, yet.

Frank

Am Montag, den 02.11.2020, 13:37 -0600 schrieb Barry Revzin:


On Thu, Jun 11, 2020 at 1:29 PM Frank Zingsheim via Std-Proposals <std-proposals@lists.isocpp.org> wrote:
I have added a paragraph "Comparison to Alternatives already available
in C++17"

This paragraph contains a comparison of the fold conditional operator
to a recursive definition as well as to the usage of the fold with
operator|| trick.

Please find the updated version on github:
https://github.com/zingsheim/ProposalTernaryFold/blob/progress/Proposal
TernaryFold.md

The first motivating example in the paper is the ability to be able to write:

template <std::size_t... is>
T test_impl(std::size_t j, std::index_sequence<is...>)
{
    return ( (j == is) ? f<is>() 
            : ... : throw std::range_error("Out of range") );
}
template <std::size_t n>
T test(std::size_t j)
{
    return test_impl(j, std::make_index_sequence<n>());
}

But we have another language feature in the pipeline, expansion statements (P1306), that could let me write this differently:

template <std::size_t n>
T test(std::size_t j)
{
    template for (constexpr std::size_t i : std::views::iota(0zu, n)) {
        if (i == j) {
            return f<i>();
        }
    }
    throw std::range_error("Out of range");
}

I think using the expansion statement is a more straightforward implementation that's easier to read.

Another motivating example from the paper is the one using the template parameter pack of translators:

template<class... translators>
std::string translate_to_english_impl(
    std::string_view language,
    std::string_view text)
{
    return ( language == translators::language
             ? translators::translate_to_english(text)
             : ... : throw std::invalid_argument(
                         std::string("Unknown language: ").append(
                             language.begin(),
                             language.end())) );
}

Which if we adopt a flavor of expansion statement that could directly iterate over a pack (I believe Richard Smith or Andrew Sutton suggested for ... at some point, don't remember who):

template<class... translators>
std::string translate_to_english_impl(
    std::string_view language,
    std::string_view text)
{
    for ... {
        if (language == translators::language) {
            return translators::translate_to_english(text);
        }
    }
    throw /* ... */;
}

Which again seems to me to be easier to read, and also just generally more flexible since you don't have to shove your whole logic into a single expression. One of the advantages of the conditional operator might be the ability to use it as an expression, but you can always wrap the expansion statement in an immediately invoked lambda, so it doesn't seem like a huge loss in comparison, at least at first glance.

Are there other use-cases for folding over a conditional operator that aren't as neatly solved with expansion statements?

Barry