如何修复成员函数中的“已声明”静态”但从未定义”?

时间:2019-06-16 02:49:03

标签: c++

我正在努力实现IRC客户端(我知道有点不合时宜),并且由于这种令人困惑的编译警告(g ++ 7.4.0)而陷入困境。在clang下根本不会发生这种情况,但是我更喜欢g ++,并且希望尽可能地安抚它。

In file included from src/client/server_context.cpp:3:0:
src/protocol/numeric.h:52:15: error: ‘silica::protocol::prototype
    silica::protocol::numeric_impl<NumericCode, SymbolicName, NumArgs>::
    getPrototype() const [with const char* NumericCode =
    (& silica::protocol::NUM_RPL_ENDOFMOTD);
    const char* SymbolicName = (& silica::protocol::SYM_RPL_ENDOFMOTD);
    int NumArgs = 0]’ declared ‘static’ but never defined [-Werror=unused-function]
    prototype getPrototype() const override { return s_prototype; }

该模板的其他实例存在重复。

但是,我完全不知道函数是静态的(我想它是在谈论翻译单元专有的可见性,而不是面向对象的“静态”)还是不确定的(如何定义是正确的)在那里!)。

包含此方法的模板已在我的代码的其他地方成功实例化,并且仅当我在此特定上下文中调用getPrototype()时,才会发生上述呕吐。

现在,一些上下文:

  • numeric_impl是用于定义RFC 1459数字代码的模板类
  • numeric_impl的每个实例都源自numeric_base
  • numeric_base源自protobase(代表协议消息的基类)
  • 对于每种唯一类型的消息,该消息都有一个prototype(基本上只是一个包含其名称和消息类型的其他属性的结构)
  • protobase定义了virtual prototype getPrototype(),因此我可以在运行时确定特定消息的类型
  • 每种特定的消息类型也有一个static prototype Prototype(),它对于检查someMessage.getPrototype() == some_message_type::Prototype()之类的东西很有用

触发错误的代码基本上如下:

void server_context::connect(/* omitted irrelevant args */) {

    // Omitted irrelevant code that attempts to connect to the server

    // This call waits for a reply to the command that was sent above
    std::shared_ptr<protobase> res = client->wait_for(
        {err_nonicknamegiven::Prototype(), err_erroneusnickname::Prototype(), err_nicknameinuse::Prototype(),
         err_nickcollision::Prototype(), err_needmoreparams::Prototype(), err_alreadyregistred::Prototype(),
         rpl_endofmotd::Prototype()},
        std::chrono::seconds(5));

    // Ultimately, i want to check that 'res' was of a particular type,
    // which i intended to do by comparing its prototype against one of
    // the above prototypes, but this is a stand-in for that.
    // Removing this line makes the warning go away.
    std::cout << res->getPrototype().commandSymbol() << std::endl;

    return res;
}

值得注意的是,激怒此处编译器的getPrototype调用是在protobase而不是实例化numeric_impl上进行的,这会使警告更加混乱。

有趣的是,删除上面的getPrototype调用将导致不发出警告。另外,创建所有使用的数字类型的实例(并在这些数字类型上调用getPrototype)也会使其消失:

std::cout << err_nonicknamegiven(std::list<std::string>{}).getPrototype().commandSymbol() << std::endl;
std::cout << err_erroneusnickname(std::list<std::string>{}).getPrototype().commandSymbol() << std::endl;
std::cout << err_nicknameinuse(std::list<std::string>{}).getPrototype().commandSymbol() << std::endl;
std::cout << err_nickcollision(std::list<std::string>{}).getPrototype().commandSymbol() << std::endl;
std::cout << err_alreadyregistred(std::list<std::string>{}).getPrototype().commandSymbol() << std::endl;
std::cout << rpl_endofmotd(std::list<std::string>{}).getPrototype().commandSymbol() << std::endl;
std::cout << err_needmoreparams(std::list<std::string>{}).getPrototype().commandSymbol() << std::endl;

这种解决方法显然不是很实用,但是它使我相信该错误仅与在这种情况下使用这些类型的静态方法有关。似乎模板已实例化,编译器因此意识到了getPrototype调用,但并未为其生成代码(导致错误的“从未定义”位)。

下面是一个成功使用getPrototype()的示例(这次是在numeric_base上),该示例伴随另一个翻译单元中的numeric_base类:

bool numeric_base::operator==(const numeric_base& other) const {
    return getPrototype() == other.getPrototype() && m_args == other.m_args;
}

同样,这里没有提到任何特定的数字类型,因此没有什么可抱怨的。

numeric_impl的定义如下:

template <const char* NumericCode, const char* SymbolicName, int NumArgs>
class numeric_impl : public numeric_base {
public:
    template <typename Container>
    numeric_impl(Container const& args) : numeric_base(args) {}

    std::string serialize() const override {
        // omitted
    };

    prototype getPrototype() const override { return s_prototype; }

    static prototype Prototype() { return s_prototype; }

protected:
    static const prototype s_prototype;
};

template <const char* NumericCode, const char* SymbolicName, int NumArgs>
const prototype numeric_impl<NumericCode, SymbolicName, NumArgs>::s_prototype = {NumericCode, SymbolicName, NumArgs, 0,
                                                                                 false};

忽略警告就足够简单了,但是我觉得这里发生了更阴险的事情。更不用说深入了解编译器在这里的想法肯定会很好。任何见识将不胜感激。

0 个答案:

没有答案