You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

继承boost::spirit::qi::grammar的导出类Windows编译失败求助

问题描述

尝试实现一个继承自boost::spirit::qi::grammar的类并将其导出为动态库,代码如下:

#pragma once
#include <impexp.h>
#include <boost/spirit/include/qi.hpp>
#include <boost/spirit/include/phoenix.hpp>

using boost::spirit::ascii::space_type;
struct EXPORT_DLLCLASS Grammar : boost::spirit::qi::grammar< char const*, space_type >
{
   Grammar()
       : Grammar::base_type( expr )
   {
   }
   boost::spirit::qi::rule< char const*, space_type > expr, term;
};

EXPORT_DLLCLASS会根据平台分别定义为__attribute__( ( visibility( "default" ) ) )(Linux)或__declspec( dllexport )(Windows)。该代码在Linux平台编译成功,但在Windows平台编译时出现如下错误:

C:\.conan\oq2odq\1\include\boost/proto/extends.hpp(536,17):  error: call to implicitly-deleted default constructor of 'boost::proto::exprns_::expr<boost::proto::tagns_::tag::terminal, boost::proto::argsns_::term<boost::spirit::qi::reference<const boost::spirit::qi::rule<const char *, boost::proto::exprns_::expr<boost::proto::tagns_::tag::terminal, boost::proto::argsns_::term<boost::spirit::tag::char_code<boost::spirit::tag::space, boost::spirit::char_encoding::ascii> >, 0>, boost::spirit::unused_type, boost::spirit::unused_type, boost::spirit::unused_type> > >, 0>'
              : proto_expr_()
                ^
C:\.conan\oq2odq\1\include\boost/proto/proto_fwd.hpp(377,16):  note: in instantiation of member function 'boost::proto::exprns_::extends<boost::proto::exprns_::expr<boost::proto::tagns_::tag::terminal, boost::proto::argsns_::term<boost::spirit::qi::reference<const boost::spirit::qi::rule<const char *, boost::proto::exprns_::expr<boost::proto::tagns_::tag::terminal, boost::proto::argsns_::term<boost::spirit::tag::char_code<boost::spirit::tag::space, boost::spirit::char_encoding::ascii> >, 0>, boost::spirit::unused_type, boost::spirit::unused_type, boost::spirit::unused_type> > >, 0>, boost::spirit::qi::grammar<const char *, boost::proto::exprns_::expr<boost::proto::tagns_::tag::terminal, boost::proto::argsns_::term<boost::spirit::tag::char_code<boost::spirit::tag::space, boost::spirit::char_encoding::ascii> >, 0>, boost::spirit::unused_type, boost::spirit::unused_type, boost::spirit::unused_type>, boost::proto::domainns_::default_domain, 0>::extends' requested here
        struct extends;
               ^
C:\.conan\oq2odq\1\include\boost/proto/detail/preprocessed/expr_variadic.hpp(50,49):  note: default constructor of 'expr<boost::proto::tagns_::tag::terminal, boost::proto::argsns_::term<boost::spirit::qi::reference<const boost::spirit::qi::rule<const char *, boost::proto::exprns_::expr<boost::proto::tagns_::tag::terminal, boost::proto::argsns_::term<boost::spirit::tag::char_code<boost::spirit::tag::space, boost::spirit::char_encoding::ascii> >, 0>, boost::spirit::unused_type, boost::spirit::unused_type, boost::spirit::unused_type> > >, 0>' is implicitly deleted because field 'child0' has no default constructor
        typedef Arg0 proto_child0; proto_child0 child0;

不添加EXPORT_DLLCLASS则编译正常,请问该如何解决此问题?

解决方案

问题根源

Windows下__declspec(dllexport)标记整个类时,编译器会强制生成类的默认构造函数、拷贝构造函数等特殊成员函数。但boost::spirit::qi::grammar内部依赖Boost Proto库的类型,这些类型没有默认构造函数,导致编译器无法自动生成所需的特殊成员,从而触发编译错误。Linux平台的可见性标记不会强制生成这些特殊成员,因此没有问题。

具体解决方法

方法1:仅导出类的接口方法而非整个类

将EXPORT_DLLCLASS从类声明移到需要导出的成员函数前,避免编译器尝试生成整个类的特殊成员:

#pragma once
#include <impexp.h>
#include <boost/spirit/include/qi.hpp>
#include <boost/spirit/include/phoenix.hpp>

using boost::spirit::ascii::space_type;
struct Grammar : boost::spirit::qi::grammar< char const*, space_type >
{
   EXPORT_DLLCLASS Grammar()
       : Grammar::base_type( expr )
   {
   }
   boost::spirit::qi::rule< char const*, space_type > expr, term;
};

这种方式仅导出构造函数,减少编译器需要处理的导出内容,避免触发Proto类型的构造问题。

方法2:使用Pimpl模式隔离Boost类型

将包含Boost Spirit类型的实现细节隐藏到内部类中,对外仅导出一个包含指针的轻量级类,彻底避免导出复杂的Boost类型:

头文件(对外接口):

#pragma once
#include <impexp.h>

struct GrammarImpl;

struct EXPORT_DLLCLASS Grammar
{
    Grammar();
    ~Grammar();
    // 如需对外暴露解析方法,可在此声明并转发到Impl
    bool parse(const char* input);

private:
    GrammarImpl* pImpl;
};

实现文件(内部实现):

#include "Grammar.h"
#include <boost/spirit/include/qi.hpp>
#include <boost/spirit/include/phoenix.hpp>

using boost::spirit::ascii::space_type;

struct GrammarImpl : boost::spirit::qi::grammar< char const*, space_type >
{
    GrammarImpl() : base_type(expr)
    {
        // 这里编写实际的Grammar规则定义
    }
    boost::spirit::qi::rule< char const*, space_type > expr, term;
};

Grammar::Grammar() : pImpl(new GrammarImpl()) {}
Grammar::~Grammar() { delete pImpl; }

bool Grammar::parse(const char* input)
{
    // 转发到Impl的解析逻辑
    return boost::spirit::qi::phrase_parse(input, input + strlen(input), *pImpl, space_type());
}

Pimpl模式完全隔离了Boost类型的导出问题,是Windows下导出包含复杂第三方库类型的类的通用解决方案。


内容的提问来源于stack exchange,提问作者t_watcher

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.07.28 09:45:02