代码之家  ›  专栏  ›  技术社区  ›  André Winston

constexpr构造函数的参数类型“std::function”不是文本类型

  •  1
  • André Winston  · 技术社区  · 4 年前

    我正在编写一个简单的C++ HTTP服务器框架。在我的 Server 同学们,我可以加上 Route 每个路由都由一个路径、一个HTTP方法和一个 Controller (这是请求发出时要调用的函数管道) 控制器 类是通过接收 std::function 的(或者更准确地说: std::function<void(const HTTPRequest&, HTTPResponse&, Context&)> ),但大多数时候(或者我应该说 每一个 时间),这个 控制器 将使用lambda函数文本列表进行初始化,如下代码所示:

    server.add_route("/", HTTPMethod::GET,
                    {
                        [](auto, auto& response, auto&) {
                            const int ok  = 200;
                            response.set_status(ok);
                            response << "[{ \"test1\": \"1\" },";
                            response["Content-Type"] = "text/json; charset=utf-8";
                        },
                        [](auto, auto& response, auto&) {
                            response << "{ \"test2\": \"2\" }]";
                        },
                    }
            );
    

    既然如此,我想 add_route 函数a constexpr ,因为,如果我错了,请纠正我, 常量表达式 函数可以在编译时执行。

    所以,当我做一切的时候 常量表达式 ,我发现以下错误:

    Controller.cpp:9:1 constexpr constructor's 1st parameter type 'Callable' (aka 'function<void (const HTTPRequest &, HTTPResponse &, Context &)>') is not a literal type
    

    我想知道的是:为什么 std::函数 不能是文本类型吗?有没有办法绕过这个限制?

    下面是的代码 控制器 班级。我知道还有其他编译错误,但这是我现在要解决的主要问题。提前谢谢!

    controller.hpp

    #pragma once
    
    #include <functional>
    #include <initializer_list>
    #include <vector>
    
    #include "context.hpp"
    #include "httprequest.hpp"
    #include "httpresponse.hpp"
    
    typedef std::function<void(const HTTPRequest&, HTTPResponse&, Context&)> Callable;
    template <size_t N>
    class Controller {
    private:
        std::array<Callable, N> callables;
    
    public:
        static auto empty_controller() -> Controller<1>;
    
        constexpr explicit Controller(Callable);
        constexpr Controller();
        constexpr Controller(std::initializer_list<Callable>);
    
        void call(const HTTPRequest&, HTTPResponse&, Context&);
    };
    

    controller.cpp

    #include "controller.hpp"
    
    template <size_t N>
    auto Controller<N>::empty_controller() -> Controller<1> {
        return Controller<1>([](auto, auto, auto) {});
    }
    
    template <>
    constexpr Controller<1>::Controller(Callable _callable) :
        callables(std::array<Callable, 1> { std::move(_callable) }) { }
    
    template <>
    constexpr Controller<1>::Controller() :
        Controller(empty_controller()) { }
    
    
    template <size_t N>
    constexpr Controller<N>::Controller(std::initializer_list<Callable> _list_callables) :
        callables(_list_callables) { }
    
    template <size_t N>
    void Controller<N>::call(const HTTPRequest& req, HTTPResponse& res, Context& ctx) {
        for (auto& callable : callables) {
            callable(req, res, ctx);
        }
    }
    
    0 回复  |  直到 4 年前
        1
  •  2
  •   Quimby    4 年前

    为什么std::函数不能是文本类型?有没有办法绕过这个限制?

    因为它使用类型擦除来接受任何可调用的。这需要多态性,它不能是COSTEXPR直到C++ 20允许。 constexpr virtual . 您可以使用模板并直接捕获可调用对象,但它的类型将逐渐进入 Controller 并进一步扩散。

    在这种情况下,我想使add\u route函数成为constexpr,因为如果我错了,请纠正我,constexpr函数可以在编译时执行。

    对, 如果给予 constexpr 参数,则函数将在编译时执行。把它看成是高级的不断折叠。此外 常量表达式 在编译时上下文中使用的方法不能访问 *this 或者它太过复杂。特别地, 常量表达式 方法只能更改 常量表达式 编译时的实例。否则,函数将在运行时正常运行。

    最后一点与您有关,在编译时运行HTTP服务器几乎没有意义,因此 常量表达式 可能不需要,也没什么用。

    编辑 常量表达式 行为示例

    struct Foo{
        //If all members are trivial enough and initialized, the constructor is constexpr by default.
        int state=10;
        //constexpr Foo()=default;
    constexpr int bar(bool use_state){
        if(use_state)
            return state++;
        else
            return 0;// Literal
    }
    constexpr int get_state()const{
        return state;
    }
    };
    
    template<int arg>
    void baz(){}
    int main(int argc, char* argv[])
    {
       Foo foo;
       //Carefull, this also implies const and ::bar() is non-const.
       constexpr Foo c_foo;
    
       foo.bar(true);//Run-time, `this` is not constexpr even though `true` is
       foo.bar(false);//Compile-time, `this` was not needed, `false` is constexpr
    
       bool* b = new bool{false};
       foo.bar(*b);//Always run-time since `*b` is not constexpr
    
    
    
       //Force compile-time evaluation in compile-time context
       //Foo has constexpr constructor, creates non-const (temporary) constexpr instance
       baz<Foo().bar(true)>();
       baz<Foo().bar(false)>();
       baz<foo.bar(false)>();
       //ERROR, foo is not constexpr
       //baz<foo.bar(true)>();
       //ERROR, c_foo is const
       //baz<c_foo.bar(false)>();
       //Okay, c_foo is constexpr
       baz<c_foo.get_state()>();
       //ERROR, foo is not constexpr
       //baz<foo.get_state()>();
    
        return 0;
    }