将 PIMPL 习惯用法与成员函数模板一起使用(无需预先了解所有可能的数据类型)
Using the PIMPL idiom with member function templates (without knowing all possible data types up-front)
我正在尝试使用 PIMPL 习惯用法实现一个通用接口,该习惯用法需要一些模板成员函数来为任何类型的功能。问题是我找不到任何方法来支持将模板函数与 PIMPL 习惯用法一起使用,而无需预先声明函数可能使用的每个可能类型。
请参阅相关问题: 带有模板成员函数的痘痘成语
该问题的公认答案建议显式定义将与模板一起使用的每个类型。这并不总是实用的。请考虑以下示例:
例子.h
#ifndef EXAMPLE_H
#define EXAMPLE_H
#include <memory>
class Example
{
public:
Example();
~Example();
// Generic template function in the interface
template <typename T>
void PrintData(const T& data) const;
private:
struct Impl;
std::unique_ptr<Impl> m_impl;
};
#endif
示例.cpp
#include "example.h"
#include <iostream>
// Private implementation
struct Example::Impl
{
template <typename T>
void PrintData(const T& data) const
{
std::cout << data << std::endl;
}
};
// Note: using C++11 so "make_unique" is not available
Example::Example() : m_impl(new Impl()) {}
Example::~Example() = default;
// Forward the method call to the implementation
template <typename T>
void Example::PrintData(const T& data) const
{
m_impl->PrintData(data);
}
// Don't want to have to explicitly state all types...
// this is not practical because "PrintData" should work for ANY printable type.
// Uncommenting the line below will allow this example to compile,
// but it will only compile when used with "int" data.
//template void Example::PrintData(const int& data) const;
主.cpp
#include "example.h"
int main()
{
Example ex;
ex.PrintData(42);
}
尝试编译此内容将失败,并显示未定义的引用错误:
g++ -std=c++11 -o main main.cpp example.cpp
/tmp/cc6IhZsx.o: In function `main':
main.cpp:(.text+0x2b): undefined reference to `void Example::PrintData<int>(int const&) const'
collect2: error: ld returned 1 exit status
此示例在界面中提供了一个非常通用的函数,该函数应该适用于任何可打印类型。这带来了一个问题,因为我们无法预测在编译时之前将使用此函数的所有类型。
我怎样才能让这样的函数与 PIMPL 习惯用法(或其他创建"编译器防火墙"的方法)一起使用
请注意,此示例有意用于演示问题。在现实世界中,我的类在界面中包含更复杂的模板函数,需要处理许多类型。因此,我无法像 PIMPL 习语那样将这些类放在编译器防火墙后面。
我愿意考虑 PIMPL 习语的替代方案,如果存在任何这样的事情,它可以达到预期的效果。
如果必须使用 PImpl 习惯用法并且必须支持任何类型,则最好的办法就是键入擦除行为。这允许您隐藏部分实现,但不是全部。
对于简单情况,std::function
或建议的std::function_ref
可以工作:
#include <memory>
#include <ostream> // We must define some of the behavior in the header file.
#include <functional> // for std::function
class Example
{
public:
Example();
~Example();
// Generic template function in the interface
template <typename T>
void PrintData(const T& data) const;
private:
struct Impl;
std::unique_ptr<Impl> m_impl;
// Some alternatives:
// Some cost:
void PrintDataImpl(const void* obj,
const std::function<void(std::ostream&, const void*)>&) const;
// Not yet standardized:
void PrintDataImpl(const void* obj,
std::function_ref<void(sstd::ostream&, const void*)>) const;
// C-style:
void PrintDataImpl(const void* obj,
void(*fn)(std::ostream&, const void*)) const;
};
template <typename T>
void Example::PrintData(const T& data) const
{
// std::function is expensive, but in this case, we fit in the small buffer
// optimization as a lambda with no captures.
// If we used `std::function_ref`, this would be cheaper.
// If we used the C-style, the lambda cannot capture anything.
PrintDataImpl(&data, [](std::ostream& out, const void* obj) {
out << *static_cast<const T*>(obj);
});
}
// In Example.cpp:
void Example::PrintDataImpl(const void* obj,
const std::function<void(std::ostream&, const void*)>& fn) const
{
// Call the function to apply the one piece of type-erased behavior.
fn(std::cout, obj);
std::cout << std::endl;
}
相关文章:
- 有可能在Armadillo中复制MATLAB circshift方法吗
- 在他自己的方法中,有可能将一个对象取消引用到另一个对象吗
- 有可能使shared_ptr协变吗
- 有可能在信号处理程序中设置promise吗
- 是否有可能实现O(N)时间和O(1)空间解决方案,以实现C++中的字符串循环移位
- 是否有可能构建面向Linux和Windows的.Net Core C++ / CLI应用程序?
- 是否有可能使用debug_info获取ELF文件的源代码?
- C++,是否有可能/如何定义在.h和.cpp源文件中调用函数的类构造函数
- 有可能在C++中有类的查找表吗
- 是否有可能让 c++ dll 在后台运行 python 程序并让它填充向量图?如果是这样,如何?
- 向量的大小是否有可能为 1 但其中的元素数量为零?
- 是否有可能编写新的叮当声现代化规则?
- 是否有可能通过指向另一个未关联的子对象的指针来获取指向一个子对象的指针?
- 是否有可能通过演绎指南实现整个 std::make_tuple 功能?
- 是否有可能在没有复制的情况下传递 std::vector<int> 作为参数来获得 std::vector<std::array<int, 3>>?
- 是否有可能具有放入容器的移动操作的类型?
- 是否有可能通过溢出 C 中的第一个元素来写入数组第二个元素
- 声明是否有可能逃脱其封闭的名称空间
- git-是否有互联网上某处所有可能错误的列表
- 是否有可能有一个派生类继承最终函数但创建相同的函数(而不是重写)