1

在某些语言中,调用函数时可以省略括号。

frobnicate foo, bar, baz, qux

让我们看看我们是否可以将它或类似的东西带入 C++。这几乎肯定意味着滥用逗号运算符,但是,据我所知,没有办法扩展任意类型以具有重载的逗号运算符。所以像代理 currier 类型的东西可以工作。

comma_curry(frobnicate), foo, bar, baz, qux;

comma_curry(我不擅长提出名称)可能是某种增量 currier,其逗号运算符一次接受一个参数。理想情况下,我希望它具有完美的转发语义,这样它就与简单地直接调用没有什么不同frobnicate

frobnicate(foo, bar, baz, qux);

特别是如果某些参数本身是函数调用,它可能会返回纯右值。

frobnicate(foo(), bar, baz(), qux);

我使用从另一篇文章中复制的一些帮助代码,玩弄并提出了一个无效的示例。有人有一些好的或更好的想法吗?

4

2 回答 2

2

当然,所有这些对于普通的函数参数来说都是微不足道的。

除非用于学习和玩具示例,否则我不建议使用逗号运算符。不要把它放在生产代码中。

但是使用逗号操作符对于增量和 re-currying 也很有趣。这是一个工作示例:

template<typename F, typename... Curry>
struct comma_curry {
    template<typename... C>
    explicit comma_curry(F function, C&&... c) noexcept :
       function(std::move(function)), curry{std::forward<C>(c)...} {}

    template<typename T>
    friend auto operator,(comma_curry&& self, T&& arg) -> comma_curry<F, Curry..., std::decay_t<T>> {
        return std::apply([&](auto&&... curry) {
            return comma_curry<F, Curry..., std::decay_t<T>>(std::move(self.function), std::forward<decltype(curry)>(curry)..., std::forward<T>(arg));
        }, std::move(self.curry));
    }

    template<typename... Args>
    auto operator()(Args&&... args) const& -> decltype(auto) {
        return std::apply([&](auto&&... curry) -> decltype(auto) {
            return function(std::forward<decltype(curry)>(curry)..., std::forward<Args>(args)...);
        }, curry);
    }

    template<typename... Args>
    auto operator()(Args&&... args) && -> decltype(auto) {
        return std::apply([&](auto&&... curry) -> decltype(auto) {
            return std::move(function)(std::forward<decltype(curry)>(curry)..., std::forward<Args>(args)...);
        }, std::move(curry));
    }

private:
    // [[no_unique_address]]
    F function;

    // [[no_unique_address]]
    std::tuple<Curry...> curry;
};

它支持通过currying引用std::ref并支持currying move only types。

它可以这样使用:

int main() {
    auto function = [](int& i, double, std::unique_ptr<int>, std::tuple<int>) {
        std::cout << "Called! i value: " << i << std::endl;
    };

    int number = 1;

    // Reference arguments are sent using std::ref
    auto curried = (comma_curry(function), std::ref(number), 1.5);
    curried(std::make_unique<int>(), std::tuple{1});

    number = 42;
    auto recurried = (std::move(curried), std::make_unique<int>(), std::tuple{1});

    // We curried a std::unique_ptr, our function is a one time call
    // Since it's a one time call and destroy itself in the calling,
    // it must be moved
    std::move(recurried)();
}

活生生的例子

于 2019-11-20T16:25:37.863 回答
1

这是我如何做到这一点的要点。请注意,它仅适用于单参数函数,但可以轻松扩展为柯里化。我使用标签类型来确保选择正确的重载:

#include <functional>
#include <type_traits>
#include <utility>

void print_thing(int a) {
}

struct go_t{} go; 

template<class F>
auto operator,(go_t, F f) {
  return [a=std::move(f)](const auto& b) {
    return a(b);
  };
}

template<class T>
auto operator,(const std::function<void(T)>& f, T&& t) {
    return f(std::forward<T>(t));
}

int main() {
    // Make this happen!
    (go, print_thing, 42);
}
于 2019-11-20T16:11:09.723 回答