c++ 如何解包长度为1的参数包(包含单个值)?

px9o7tmv  于 2023-01-10  发布在  其他
关注(0)|答案(3)|浏览(80)

我正在写一个小的变元求和函数(使用c20,但我的问题在c17语法下仍然是一样的)。我希望让下面的代码尽可能简短和清晰(但不使用折叠表达式。这只是一个玩具问题,但在以后的应用程序中我希望避免使用折叠表达式):

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表达式两边加上括号,我会得到以下错误:* 在“)”标记 * 之前需要二元运算符。
有没有办法把长度为1的参数包传递给操作符?

qnakjoqk

qnakjoqk1#

拥抱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 参数工作,但问题随之而来:为了避免出现这种情况,我没有定义这个例子。)如果你坚持sum只能从arity 2开始定义,你可以使用下面的方法:

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

然而,只要有意义,你就应该允许使用“vacuous”格:它使代码更简单、更通用。注意例如在后一个定义中加法运算符出现了两次:这实际上是在两种情况之间复制折叠逻辑(在这种情况下,它只是一个加法,所以相对简单,但对于更复杂的运算,它可能会更麻烦),而处理退化情况通常是微不足道的,不会复制任何东西。
(我省略了概念注解,因为它们似乎与主要问题并不特别相关。)

puruo6ea

puruo6ea2#

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


题外话:由于不确定Op的真实的需求,我偶然地快速设计了一个我一直在思考的东西。

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

〉_〈

zzoitvuj

zzoitvuj3#

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

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

相关问题