运算符旁边的参数解包

问题描述

我正在编写一个小的可变参数求和函数(使用 c++20,但我的问题将与 c++17 语法保持不变)。我想让下面的代码尽可能简洁明了(但不使用折叠表达式。这只是一个玩具问题,但在以后的应用程序中我想避免折叠表达式):

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

使用第 (1) 行可以编译上述代码,但它需要定义 'add' lambda。但是,第 (2) 行无法编译,我在使用 gcc 时出现以下错误参数包未用‘...’扩展。如果我在第 (2) 行的 std::forward 表达式周围添加括号,我会收到以下错误expected binary operator before ‘)’ token

有没有办法将长度为 1 的参数包传递给运算符?

解决方法

拥抱 negative thinking 的力量并从零而不是一开始归纳:

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

上述定义的副作用是 sum(x) 现在将编译并返回 x。 (事实上​​,你甚至可以让函数使用 no 参数,通过让它返回零,但问题出现了:哪种类型的零?为了避免去那里,我离开了这个案例未定义。)如果您坚持只从第 2 行开始定义 sum,您可以改用它:

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

然而,只要有必要,您就应该允许“空”的情况:它使代码更简单和更通用。例如,请注意在后一个定义中加法运算符如何出现两次:这有效地复制了两种情况之间的折叠逻辑(在这种情况下只是一次加法,因此相对简单,但如果操作更复杂,则可能会更麻烦) ,而处理退化情况通常是微不足道的,不会重复任何事情。

(我省略了概念注释,因为它们似乎与主要问题没有特别的关系。)

,
template<class... Additive> decltype(auto) sum(Additive &&...val) {
    return (std::forward<Additive>(val) + ...);
}

Offtopic:不确定 Op 的真正需求,我不时不小心快速设计了一件我一直在想的事情。 :D

#include <iostream>
#include <functional>
#include <type_traits>

template<class... Fs> struct Overloads;

template<class F,class... Fs> struct Overloads<F,Fs...>: Overloads<Fs...> {
        using Fallback = Overloads<Fs...>;

        constexpr Overloads(F &&f,Fs &&...fs): Fallback(std::forward<Fs>(fs)...),f(std::forward<F>(f)) {}

        template<class... Args> constexpr decltype(auto) operator()(Args &&...args) const {
                if constexpr(std::is_invocable_v<F,Args...>) return std::invoke(f,std::forward<Args>(args)...);
                else return Fallback::operator()(std::forward<Args>(args)...);
        }
private:
        F f;
};

template<class... Fs> Overloads(Fs &&...fs) -> Overloads<Fs...>;

template<class F> struct Overloads<F> {
        constexpr Overloads(F &&f): f(std::forward<F>(f)) {}

        template<class... Args> constexpr decltype(auto) operator()(Args &&...args) const {
                return std::invoke(f,std::forward<Args>(args)...);
        }
private:
        F f;
};

template<> struct Overloads<> {
        template<class... Args> constexpr void operator()(Args &&...) const noexcept {}
};

constexpr int f(int x,int y) noexcept { return x + y; }

void g(int x) { std::cout << x << '\n'; }

template<class... Vals> decltype(auto) omg(Vals &&...vals) {
        static constexpr auto fg = Overloads(f,g);
        return fg(std::forward<Vals>(vals)...);
}

int main() {
        omg(omg(40,2));
}

>_

,

您可以将一个项目解包到一个变量中并使用它:

if constexpr (sizeof...(vals) == 1) {
    auto&& only_value(std::forward<decltype(vals)>(vals)...);
    return val + only_value;
}