【问题标题】:Work around lacking const-correctness of external library解决外部库缺乏 const 正确性的问题
【发布时间】:2017-09-14 09:22:32
【问题描述】:

我正在使用缺乏 const 正确性的外部 C++ 库。假设我正在使用以下类的对象:

// Library.h
namespace Library {

class Message {
 public:
    std::string getData() {
        return data_;
    }
 private:
    std::string data_;
};

}  // namespace Library

注意getData() 返回一个副本,因此对该方法的调用不会更改Message 对象,它应该是const。但是,供应商认为不是。 在我的代码方面,常量正确性很重要,Message 将用于如下函数:

// MyApplication.cpp

template<class T>
void handleMessage(const T& msg) {
    std::string content = msg.getData();
    // interprete and process content ...
}

有没有办法做到这一点?换句话说,如何在不更改handleMessage 函数签名的情况下解决error: passing 'const Library::Message' as 'this' argument discards qualifiers 错误

【问题讨论】:

  • 你可以使用const_cast作为最后的手段。
  • 请注意,在您的引用上通过 const_cast 剥离 const 将调用 未定义的行为,如果有两件事成立:(1) 实际上,原始的具体对象是, const, 和 (2) 对象被修改。如果 其中一个false (就像你的......现在),它确实调用未定义的行为。无论哪种方式,这都会给编码人员带来巨大的负担,因为故意编写一些当时看似“安全”的东西是非常容易的,只是后来发现有人添加了使用传递一个const 对象;编译器没有机会捕捉到它。

标签: c++ compiler-errors const-correctness


【解决方案1】:

您还可以使用带有可变成员变量的包装器,例如:

#include <string>

class Message {
 public:
    std::string getData() {
        return data_;
    }
    Message(std::string data): data_{data} { }
 private:
    std::string data_;
};

class MessageWrapper {
 public:
    MessageWrapper(Message message): message{message} {}
    std::string getData() const {
        return message.getData();
    }
 private:
    mutable Message message;
};

template<class T>
void handleMessage(const T& msg) {
    std::string content = msg.getData();

}

int main() {
    MessageWrapper mw{{"abc"}};
    handleMessage(mw);
}

[live demo]

编辑:

为了强制 const 正确性,您可以保存从消息中检索到的数据,例如:

#include <string>
#include <optional>

class Message {
 public:
    std::string getData() {
        return data_;
    }
    Message(std::string data): data_{data} { }
 private:
    std::string data_;
};

class MessageWrapper {
 public:
    MessageWrapper(Message message): message{message} {}
    std::string getData() const {
        return (data)?(*data):(*(data = message.getData()));
    }
 private:
    mutable Message message;
    mutable std::optional<std::string> data;
};

template<class T>
void handleMessage(const T& msg) {
    std::string content = msg.getData();

}

int main() {
    MessageWrapper mw{{"abc"}};
    handleMessage(mw);
}

[live demo]

【讨论】:

  • 也是一个选项,但mutable 也是 constness 作弊
  • 这应该是规范的打补丁方式。
  • @tobi303 这不是因为message 是可变的,所以它在您使用变量mw 时一直是可变的。
  • 我并不是说它有什么问题,但您仍然在看似 const 的东西上调用非 const 方法。如果库决定真正修改该方法中的可观察状态,客户端代码可能会中断,我认为没有任何方法可以完全避免这种情况
  • 可变显然是宏的前进方向。它不仅可以保护自己免受 getData() 实际更改对象的影响,而且还为您提供了一种很好的简单方法,可以在 / 如果库已修复时删除可变对象。
【解决方案2】:

你基本上有两个选择。你可以使用const_cast

template<class T>
void handleMessage(const T& msg) {
    std::string content = const_cast<T&>(msg).getData();
    // interprete and process content ...
}

只要您确定getData 确实没有修改任何成员,就可以了。或者,如果您不介意一些开销,您可以制作一份副本:

template<class T>
void handleMessage(const T& msg) {
    T copy = msg;
    std::string content = copy.getData();
    // interprete and process content ...
}

...或者作为第三种选择,如果您想从呼叫站点隐藏const_cast,您可以将其包装起来:

class MyMessage {
     Message msg;
public:
     std::string getData() const {
         return const_cast<Message>(msg).getData();
     }
};

【讨论】:

  • 库的getData() 已经创建了一个副本,因此将其存储到字符串中的开销很小。
  • @jotrocken 是getData() 制作的副本之上的附加副本
  • 是的,但我希望其中至少有一个会被编译器优化
猜你喜欢
  • 1970-01-01
  • 2020-02-17
  • 2012-11-15
  • 1970-01-01
  • 2014-10-08
  • 2017-07-18
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多