Welcome toVigges Developer Community-Open, Learning,Share
Welcome To Ask or Share your Answers For Others

Categories

0 votes
15.1k views
in Technique[技术] by (71.8m points)

c++ - Parameter unpacking next to operator

I am writing a little variadic summing function (using c++20, but my question would remain the same with c++17 syntax). I would like to make the following code as short and clear as possible (but without using folding expressions. This is only a toy problem, but in later applications I would like to avoid fold expressions):

Additive auto sum(Additive auto&& val, Additive auto&&... vals) {
  auto add = [](Additive auto&& val1, Additive auto&& val2) {
      return val1 + val2;
  }; // neccessary??
  if constexpr(sizeof...(vals) == 1) {
      return add(val, std::forward<decltype(vals)>(vals)...); // (1)
      //return val + std::forward<decltype(vals)>(vals)...; // (2)
    }
  else return val + sum(std::forward<decltype(vals)>(vals)...);  
}

Using line (1) the above code compiles, but it makes the definition of the 'add' lambda neccessary. Line (2), however, does not compile, I get the following error with gcc: parameter packs not expanded with ‘...’. If I add parentheses around the std::forward expression in line (2), I get the following error: expected binary operator before ‘)’ token.

Is there any way to pass a parameter pack with length 1 to an operator?


与恶龙缠斗过久,自身亦成为恶龙;凝视深渊过久,深渊将回以凝视…
Welcome To Ask or Share your Answers For Others

1 Answer

0 votes
by (71.8m points)

Embrace the power of negative thinking and start induction with zero instead of one:

auto sum(auto &&val, auto &&...vals) {
    if constexpr (sizeof...(vals) == 0)
        return val;
    else
        return val + sum(std::forward<decltype(vals)>(vals)...);  
}

The above definition has the side effect that sum(x) will now compile and return x. (In fact, you can even make the function work with no arguments, by having it return zero, but then the question arises: zero of which type? To avoid having to go there, I left this case undefined.) If you insist on sum being defined only from arity 2 upwards, you can use this instead:

auto sum(auto &&val0, auto &&val1, auto &&...vals) {
    if constexpr (sizeof...(vals) == 0)
        return val0 + val1;
    else
        return val0 + sum(std::forward<decltype(val1)>(val1),
            std::forward<decltype(vals)>(vals)...);
}

However, you should probably allow the ‘vacuous’ case whenever it makes sense to do so: it makes for simpler and more general code. Notice for example how in the latter definition the addition operator appears twice: this is effectively duplicating the folding logic between the two cases (in this case it’s just one addition, so it’s relatively simple, but with more complicated operations it might be more burdensome), whereas handling the degenerate case is usually trivial and doesn’t duplicate anything.

(I omitted concept annotations, as they do not seem particularly relevant to the main problem.)


与恶龙缠斗过久,自身亦成为恶龙;凝视深渊过久,深渊将回以凝视…
Welcome to Vigges Developer Community for programmer and developer-Open, Learning and Share
...