【发布时间】:2019-04-09 13:14:07
【问题描述】:
我们有一个客户希望访问的高度模板化的仅标头代码库。例如,假设它在标题foo.hpp 中包含Foo 类:
#ifndef FOO_HEADER
#define FOO_HEADER
#include <iostream>
template <typename T>
struct Foo {
Foo(){
// Do a bunch of expensive initialization
}
void bar(T t){
std::cout << t;
}
// Members to initialize go here...
};
#endif /* FOO_HEADER */
现在我们想让客户在不暴露核心代码和重写整个代码库的情况下尝试一组精简的功能。
一个想法是使用 PIMPL 成语来包装这个核心代码。具体来说,我们可以创建一个带有标题foo_wrapper.hpp 的FooWrapper 类:
#ifndef FOO_WRAPPER_HEADER
#define FOO_WRAPPER_HEADER
#include <memory>
struct FooWrapper {
FooWrapper();
~FooWrapper();
void bar(double t);
private:
struct Impl;
std::unique_ptr<Impl> impl;
};
#endif /* FOO_WRAPPER_HEADER */
和实现foo_wrapper.cpp:
#include "foo.hpp"
#include "foo_wrapper.hpp"
struct FooWrapper::Impl {
Foo<double> genie;
};
void FooWrapper::bar(double t){
impl->genie.bar(t);
}
FooWrapper::FooWrapper() : impl(new Impl){
}
FooWrapper::~FooWrapper() = default;
此代码按我的预期工作:https://wandbox.org/permlink/gso7mbe0UEOOPG7j
然而,有一件小事困扰着我。具体来说,实现需要额外的间接级别……我们必须定义Impl 类来保存Foo 类的成员。因此,所有操作都具有impl->genie.bar(t); 形式的这种间接寻址。
如果我们能以某种方式告诉编译器“实际上Impl 是类Foo<double>”会更好,在这种情况下,我们可以改为说impl->bar(t);。
具体来说,我正在考虑类似于typedef 或using 的方法来让它发挥作用。像
using FooWrapper::Impl = Foo<double>;
但这不会编译。那么问题来了:
- 有什么好方法可以消除这种间接性吗?
- 我应该使用更好的成语吗?
我的目标是 C++11 解决方案,但 C++14 也可以工作。要记住的重要一点是,解决方案不能在foo_wrapper.hpp 中使用标头foo.hpp。不知何故,我们必须将该代码编译到一个库中,并仅分发编译后的库和 foo_wrapper 标头。
【问题讨论】:
-
您是否担心间接的运行时成本或语法“开销”?
-
只是语法开销。我不太确定是否存在任何重大的运行时开销