是否可以将函数作为参数重载运算符?

Is it possible to overload an operator with a function as a parameter?

提问人:ModernEraCaveman 提问时间:7/6/2023 更新时间:7/6/2023 访问量:65

问:

我刚刚发现了操作员超载,我只是对它感到高兴。在测试它的用途时,我遇到了一个我似乎无法工作的案例,而且我找不到合适的资源来解决。

我正在尝试重载一个运算符,该运算符将函数作为参数。我的目标是创建一个可以执行给定的任何功能的结构(在合理范围内,例如遵循相同一般结构的提交命令)。在这种情况下,传递 or 不起作用,因为运算符的内部操作将包含一个 execute 函数。如果 or 没有匹配的成员函数,则该操作将不起作用。如在以下情况下:structclassstructclass

struct s_test1 {
     void funcA() {}
}
struct s_test2 {
     void funcB() {}
}
struct s_doer {
     friend void operator<<(auto, auto& s_input){
          s_input.funcA(); // would fail for s_test2
          s_input.funcB(); // would fail for s_test1
     }
}

最终,我想创建类似这个伪代码的东西:

void func0(int& storage) { storage = 0; }
void func1(int& storage) { storage = 1; }
void func2(int& storage) { storage = 2; }

void alt_func(int& storage, int alt) { storage = alt; }

struct s_doer {
     friend void operator<<(auto, void(*func)()){
          int storage;
          func(storage);
          cout << storage << endl; // to check if it worked, idk
     }
}

int main() {
    s_doer{} << func0();
    s_doer{} << func1();
    s_doer{} << alt_func(); // alternatively 's_doer << alt_func(), 0' where '0' is the alt parameter of 'alt_func'
}

这是我目前的实施尝试:

struct s_doer {
    friend void operator<<(auto, void (*func)()) {
        std::cout << "test" << std:endl;
        func();
        std::cout << "test" << std::endl;
    }
};
struct s_informer {
    s_informer(int n) {
        info = n;
    }
    void inform() {
        std::cout << info << std::endl;
    }
private:
    int info;
    friend void operator<<(auto, const void (*func)());
};

s_informer informer(2);

int main() {
    s_doer{} << informer.inform();
}

我似乎无法通过错误。no operator "<<" matches these operands

我的其他一些尝试是:

friend void operator<<(auto, std::function<void()> func) {...}

template<typename input>
using Func = void(*)(input in);
friend void operator<<(auto, Func func) {...}

typedef void (*Func)(int n);
friend void operator<<(auto, Func func) {...}

是否可以使操作员过载功能输入?是否可以在重载期间为函数指定参数?

怎么做?

C++ 函数 struct 运算符重载

评论

1赞 Mooing Duck 7/6/2023
alt_func需要两个参数,并且不清楚应该传递给该函数的参数operator<<

答:

1赞 HolyBlackCat 7/6/2023 #1

informer.inform()立即调用该函数,而您希望将调用延迟到 。operator<<

您可以传递一个调用它的 lambda,然后在 中调用它。operator<<

#include <functional>
#include <iostream>

struct s_doer
{
    template <typename F>
    friend void operator<<(s_doer, F &&func)
    {
        std::cout << "test\n";
        std::forward<F>(func)();
        std::cout << "test\n";
    }
};

struct s_informer
{
    s_informer(int n)
    {
        info = n;
    }
    void inform()
    {
        std::cout << info << std::endl;
    }

  private:
    int info;
};

s_informer informer(2);

int main()
{
    s_doer{} << []{informer.inform();};
}

请注意,我已经更改了要模板化的第二个参数,否则您将无法接受捕获 lambda(这有利于通用性,尽管此特定 lambda 未捕获)。operator<<

我还将第一个参数从 更改为 .前者虽然聪明,但可能会产生意想不到的副作用:autos_doer

template <typename> struct A {};

int main()
{
    A<s_doer>{} << []{informer.inform();}; // This actually compiles! But you don't want it to.
}

评论

0赞 ModernEraCaveman 7/6/2023
为什么我应该避免执行该操作?A<s_doer>{} << []{informer.inform();};
1赞 Mooing Duck 7/6/2023 #2

alt_func需要两个参数,并且不清楚应该传递给该函数的参数。但是,如果我们忽略这一点,那么你的第一次尝试就非常接近了。operator<<

friend void operator<<(auto, void(*func)())应该是.您肯定希望尽可能多地指定合理内容,这就是您要在左侧使用的原因。右侧采用指向函数的指针,这些函数各自接受一个参数,并返回 void,因此您必须指定参数。friend void operator<<(s_doer&&, void(*func)(int&))s_doer&&int&int&

然后在调用的时候,你不想传递func0的结果,所以你不想调用它,所以你不想输入。相反,您希望传递函数本身operator<<func0()s_doer{} << func0;


struct s_doer {
     friend void operator<<(s_doer&&, void(*func)(int&)){
          int storage;
          func(storage);
          cout << storage << endl; // to check if it worked, idk
     }
};

int main() {
    s_doer{} << func0;
    s_doer{} << func1;
    //s_doer{} << alt_func; // alternatively 's_doer << alt_func(), 0' where '0' is the alt parameter of 'alt_func'
}

http://coliru.stacked-crooked.com/a/e215b7629acf8a76

让一个接受具有任何签名的函数的参数几乎永远没有意义,因为没有办法调用它,因为你不知道要传递哪些参数。 因此它是无用的。但如果需要,您可以使用模板。

template<class...Args>
void operator<<(s_doer&&, void(*func)(Args&&...)) {
    func(std::forward<Args>(values)...); 
    //not sure where you plan to get `values` from, but there's ways
}

另请注意,编译器很难优化函数指针,因此我们经常避免使用它们并允许函数类型本身作为模板参数,从而允许函数类类,例如等。这些可能会导致二进制膨胀,但也更容易优化。std::less

评论

2赞 HolyBlackCat 7/6/2023
operator<<使用 >2 参数将无法编译。
0赞 ModernEraCaveman 7/6/2023
用参数调用运算符的正确约定是什么? ?s_doer{} << alt_func, 2;
0赞 Mooing Duck 7/6/2023
我已经删除了那些额外的参数
2赞 Ted Lyngmo 7/6/2023 #3

我正在抛出一个替代方案,它构建了 s 的 a,并在完整表达式的末尾调用所有收集的函数。对于需要参数的函数,您可以将它们打包到 lambda 中。std::tuplestd::function

#include <functional>
#include <iostream>
#include <tuple>
#include <utility>

template <class T = std::tuple<>>
struct s_doer {
    ~s_doer() {
        // time to die, call the collected functions:
        std::apply([](auto&&... funcs) { ((funcs ? funcs() : void()), ...); }, t);
    }

    T t;

    template <class F>
    friend auto operator<<(s_doer&& d, F&& f) {
        std::function func{std::forward<F>(f)};
        auto tc = std::tuple_cat(std::move(d.t), std::tuple{std::move(func)});
        // return a new s_doer with the collected functions so far
        return s_doer<decltype(tc)>{std::move(tc)};
    }
};
int main() {
    s_doer{} << [] { std::cout << "Hello\n"; } << [] { std::cout << "fab\n"; };
}

输出:

Hello
fab

没有 s 的更轻量级版本:std::function

template <class T = std::tuple<>>
struct s_doer {
    ~s_doer() {
        // time to die, call the collected functions:
        if (last) std::apply([](auto&&... funcs) { (..., funcs()); }, t);
    }

    T t;
    bool last = false;

    template <class F>
    friend auto operator<<(s_doer&& d, F&& f) {
        auto tc = std::tuple_cat(std::move(d.t), std::tuple{std::forward<F>(f)});
        d.last = false;
        // return a new s_doer with the collected functions so far
        return s_doer<decltype(tc)>{std::move(tc), true};
    }
};