我们有一个客户希望访问的,带有大量模板的仅标头代码库。例如,假设它在标头Foo
中包含foo.hpp
类:
#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惯用语包装此核心代码。具体来说,我们可以使用标题FooWrapper
创建一个foo_wrapper.hpp
类:
#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.hpp
中的标头foo_wrapper.hpp
。我们必须以某种方式将这些代码编译到一个库中,然后仅分发已编译的库和foo_wrapper
头。
答案 0 :(得分:3)
您可以在Foo
中向前声明FooWrapper.h
。这将允许您为其声明std::unique_ptr
:
#ifndef FOO_WRAPPER_HEADER
#define FOO_WRAPPER_HEADER
#include <memory>
// Forward declaration
template <typename T>
class Foo;
struct FooWrapper {
FooWrapper();
~FooWrapper();
void bar(double t);
private:
std::unique_ptr<Foo<double>> impl;
};
#endif /* FOO_WRAPPER_HEADER */
foo_wrapper.cc
:
#include "foo_wrapper.h"
#include "foo.h"
void FooWrapper::bar(double t) {
impl->bar(t);
}
FooWrapper::FooWrapper() : impl(std::make_unique<Foo<double>>()) {}
FooWrapper::~FooWrapper() = default;
答案 1 :(得分:1)
只需使用Foo<double>
:
// forward declaration so that you don't need to include "Foo.hpp"
template class Foo<double>;
struct FooWrapper {
//...
std::unique_ptr<Foo<double>> impl;
};
// explicit template instantiation so that Foo<double> exists without distributing "Foo.hpp"
template class Foo<double>;
void FooWrapper::bar(double t){
impl->bar(t);
}