(可选)基于可变模板参数发布方法
Optionally publish methods based on variadic template parameters
前言
想象一下,我有一个模板: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
)将从第一个版本开始填充(由一些线程重写记录,并转换为平面表示),但重要的是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()
),该虚拟偏移总是在增长(随着每个记录),其模拟大小为例如4GB的虚拟存储器(当unsigned
用作偏移时,它可以使用size_t
或unisgned long long
更大),其中实际缓冲区(大小为例如4096B)在该虚拟存储器内创建窗口。
链接到我的另一个相关问题-选项包帮助程序,它是专门为这个模板设计的
(请注意,可能还有许多其他功能可以独立组合,例如opt::rqueue_listener
,可用于报告各种事件)
问题
我已经设法创建了具有所有可能功能的模板,其中一些方法在未选择功能时是伪(例如,在这种情况下,start()
返回零,stop()
与size()
相同),但如果未选择功能,我希望以某种方式隐藏这些方法知道吗
(如果不包括opt::rqueue_listener
,则另一个示例为伪set_listener(void*)
-该选项可以与任何其他选项组合。)
编辑:想象一下,例如using off_t = conditional_t<something,the_type,void>
和private: off_t start_()
。我想要的是:
- 如果
off_t
不是void
,则让public: off_t start()
调用该start_()
- 如果
off_t
无效(或满足某些条件),则没有方法start()
。或者,如果我尝试调用static_assert
我的尝试
我正在考虑将该类与扩展程序合并,后者将通过将自身(*this
)强制转换为真实类(rqueue<...>&
)并将调用重定向到那里(重定向到私有方法,在那里扩展器是朋友)来发布函数。我创建了另一个template<class... Bases> class merge
帮助程序,它可以继承任何选定的类,同时忽略任何传递的void
。它起了作用,但解决方案相当丑陋,我不喜欢它。
我想到的另一个可能的解决方案是创建一些基本的实现(作为一个不同的模板,可能隐藏在一些namespace detail
中),并使用一系列模板专门化来发布基于选项的方法。问题是组合的数量正在快速增长,friend
允许类访问记录的私有方法(传递给模板的第一个参数)可能会出现另一个问题。
我的SFINAE和static_assert
尝试经常以编译器错误告终,抱怨模板中不允许方法专门化(或部分专门化),或者static_assert在不应该被激发的时候被激发了。我希望有一些不错的解决方案。期待看到它:)
以下代码是我在收到Piotr S.的提示后所做的尝试:
(想象一下using namespace 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()
(并确保不要在模板中调用它)可以解决问题。我会等一段时间(如果有不同的答案,我很乐意接受)。
选项1
利用static_assert
以及模板的成员函数仅在上下文需要时按需实例化的事实:
#include <iostream>
#include <type_traits>
constexpr bool condition = true;
template <class... Opts>
class rqueue
{
using off_t = std::conditional_t<condition, int, void>;
public:
off_t start()
{
static_assert(!std::is_same<off_t, void>::value, "!");
return start_();
}
private:
off_t start_()
{
std::cout << "start_()" << std::endl;
return 1;
}
};
也就是说,当condition
为假时访问start()
将触发静态断言错误:
error: static assertion failed: !
演示1
选项2
使用一些丑陋的SFINAE,通过使成员函数也成为模板:
#include <iostream>
#include <type_traits>
constexpr bool condition = true;
template <class... Opts>
class rqueue
{
using off_t = std::conditional_t<condition, int, void>;
public:
template <typename T = void>
std::enable_if_t<!std::is_same<off_t, void>::value, off_t> start()
{
return start_();
}
private:
off_t start_()
{
std::cout << "start_()" << std::endl;
return 1;
}
};
这将触发错误:
error: no type named 'type' in 'struct std::enable_if<false, void>'
演示2
选项3
在CRTP中使用一些辅助基类,它有条件地实现方法:
#include <iostream>
#include <type_traits>
constexpr bool condition = true;
template <bool condition, typename CRTP>
struct start_base {};
template <typename CRTP>
struct start_base<true, CRTP>
{
int start()
{
return static_cast<CRTP*>(this)->start_();
}
};
template <class... Opts>
class rqueue : public start_base<condition, rqueue<Opts...>>
{
friend class start_base<condition, rqueue<Opts...>>;
private:
int start_()
{
std::cout << "start_()" << std::endl;
return 1;
}
};
condition = false;
上的错误消息非常清楚:
error: 'class rqueue<>' has no member named 'start'
演示3
- 为不同配置设置MSVC_RUNTIME_LIBRARY的正确方法是什么
- 通过方法访问结构
- 最小硬币更换问题(自上而下方法)
- C++为构建时间获取QDateTime的可靠方法
- 在C#中处理C++指针而不使用unsafe的最佳方法
- 处理多个异常集合的C++方法
- 如果C++类在类方法中具有动态分配,但没有构造函数/析构函数或任何非静态成员,那么它仍然是POD类型吗
- 有什么方法可以遍历结构吗
- 当类在C++中定义时,有什么方法可以"register"类吗?
- 在C++中,将大的无符号浮点数四舍五入为整数的最佳方法是什么
- 实现无开销push_back的最佳方法是什么
- 使用std::函数映射对象方法
- 有符号的int和int-有没有一种方法可以在C++中区分它们
- C++从另一个类访问公共静态向量的正确方法是什么
- C++优先级队列,按对象的唯一指针的特定方法升序排列
- 没有为自己的结构调用列表推回方法
- 有没有什么方法可以使用一个函数中定义的常量变量,也可以由c++中同一程序中的其他函数使用
- 在类定义之后定义一个私有方法
- 枚举环境变量的惯用C++14/C++17方法
- C++中的石头剪刀布循环方法