前言
假设我有一个模板:template<class... Opts> class rqueue
,它可以通过传递给参数列表的标签(特殊选项结构)选择各种功能,例如
rqueue<trace_record, opt::fixed_size<>> trace;
rqueue<trace_record::flat, opt::fixed_size<>, opt::virtual_offset<>> info;
第一个版本 (trace
) 是一个记录队列,用于写入跟踪记录(opt::fixed_size
将其大小限制为 4096B)。第二个版本 ( info
) 将从第一个版本开始填充(通过某个线程,它将重写记录并转换为 flat 表示),但重要的是 opt::virtual_offset<>
添加了这些方法:
off_t start(); // virtual offset of oldest record
off_t stop(); // virtual offset of future record (when next gets written)
和各种其他功能 ( offset_to_iterator()
) 基于此 虚拟偏移量 总是在增长(每条记录),它模拟大小的虚拟内存,例如4 GB(当 unsigned
用作偏移量时,使用 size_t
或 unisgned long long
时它可能更大),其中实际缓冲区(大小例如 4096B)在该虚拟内存中创建一个窗口。
Link to my other related question - option pack helper 专为此模板设计。
(请注意,可能还有许多其他功能可以独立组合,例如 opt::rqueue_listener
可用于报告各种事件)。
问题
我已经设法创建了具有所有可能功能的模板,其中一些方法在未选择功能时是虚拟(例如,start()
返回零,stop()
在这种情况下与 size()
相同) ,但如果未选择该功能,我想以某种方式隐藏这些方法。 有什么想法吗?
(如果不包括 set_listener(void*)
,另一个示例将是虚拟 opt::rqueue_listener
- 该选项可以与任何其他选项组合。)
编辑:想象一下,例如using off_t = conditional_t<something,the_type,void>
和 private: off_t start_()
。我想要的是:
- 如果
public: off_t start()
不是start_()
,则让off_t
调用void
- 如果
start()
无效(或满足某些条件),则没有方法off_t
。或者,如果我尝试调用它,则为static_assert
。
我的尝试
我正在考虑将该类与 extenders 合并,后者将通过将自身 ( *this
) 转换为实际类 ( rqueue<...>&
) 并将调用重定向到那里(到私有(private)方法,其中 < em>extender 成为 friend )。我创建了另一个 template<class... Bases> class merge
助手,它可以继承任何选定的类,同时忽略任何传递的 void
。它有效,但解决方案非常丑陋,我不喜欢它。
我想到的另一个可能的解决方案是创建一些基本实现(作为不同的模板,可能隐藏在某些 namespace detail
中)并使用一系列模板特化,这些模板将根据选项发布方法。问题是组合的数量正在快速增长,并且 friend
可能存在另一个问题 - 让类访问记录的私有(private)方法(传递给模板的第一个参数)。
我的 SFINAE 和 static_assert
尝试经常以编译器错误结束,提示模板中不允许方法特化(或部分特化)或者 static_assert 在不应该被触发时被触发。我希望有一些不错的解决方案。期待看到它:)
最佳答案
下面的代码是我在 Piotr S. 的提示下尝试的:
(想象一下 在该 header 中使用命名空间 std
,尽管它有点不同。)
#include "basics.hpp"
using namespace firda;
template<class Offset = void> struct helper {
Offset start_() const { return 0; }
};
template<> struct helper<void> {
void start_() const {}
};
template<class Offset = void> class rqueue
: private helper<Offset> {
public:
Offset start() const {
static_assert(!is_same<Offset,void>::value, "!!");
return this->helper<Offset>::start_();
}
};
int main() {
rqueue<> one;
rqueue<uint> two;
cout << two.start();
// one.start(); -- assert triggered
}
我在实际代码中遇到了类似 static_assert
的一些问题,但不记得为什么编译器在基本版本中触发了它……可能是我错误地在不应该的地方调用它。我认为有一个 start_()
用于内部使用(如果不使用则伪造它)并且有一个 public: start()
与断言(并确保不要在模板中调用它)解决了问题。我会等一会儿(如果有的话,我很乐意接受不同的答案)。
关于c++ - 可选地发布基于可变模板参数的方法,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/26336819/