- html - 出于某种原因,IE8 对我的 Sass 文件中继承的 html5 CSS 不友好?
- JMeter 在响应断言中使用 span 标签的问题
- html - 在 :hover and :active? 上具有不同效果的 CSS 动画
- html - 相对于居中的 html 内容固定的 CSS 重复背景?
背景信息
我正在研究类似数据流的设计模式。下面提供的类旨在表示输出数据分发机制。 level1
是CRTP的基类。 getOutput<N>
中的level1
是可用于从派生类的实例获取输出数据的函数。根据模板参数N
,它调用用户定义的方法getOutputImpl
之一。这些方法应在(CRTP样式)派生类中提供。每个方法getOutputImpl
都定义一个与用户定义的派生类相关的输出端口。 getOutputImpl
方法的输入类型是由设计定义的。 getOutputImpl
方法的输出类型可以不同。但是,根据设计,输出类型必须具有结构std::unique_ptr<TOutputType>
,其中TOutputType可以是任何类。更多背景信息可以在这里找到:previous question。
问题
为了自动识别用户定义的端口数(即getOutputImpl
方法),在getOutputPortsNumber(void)
基类中提供了level1
方法。此方法基于以下想法:所有用户定义的方法getOutputImpl
的返回类型为std::unique_ptr<TOutputType>
。因此,可以在基类中定义不具有此返回类型的另一种getOutputImpl
方法(例如,其具有void
返回类型:void getOutputImpl(...)
)。
如果在用户定义的派生类(此示例中为void getOutputImpl(...)
)中定义了DataflowOutputClass
以及其他用户定义的std::unique_ptr<TOutputType> getOutputImpl(...)
方法,则上述方法将起作用。但是,将其他void getOutputImpl(...)
方法移到基本level1
类时,出现编译错误:no matching function for call to 'DataflowOutputClass<int>::getOutputImpl(PortIdxType<2ul>, const PolyIndex&) const
。
代码
typedef size_t Index;
typedef unsigned long Natural;
typedef std::vector<Index> PolyIndex;
typedef const PolyIndex& crPolyIndex;
template<Index N> struct PortIdxType{};
template<typename TLeafType>
class level1
{
public:
TLeafType* asLeaf(void)
{return static_cast<TLeafType*>(this);}
TLeafType const* asLeaf(void) const
{return static_cast<TLeafType const*>(this);}
template <Index N>
auto getOutput(crPolyIndex c_Idx) const
{return asLeaf() -> getOutputImpl(PortIdxType<N>{}, c_Idx);}
static constexpr Natural getOutputPortsNumber(void)
{return getOutputPortsNumberImpl<0>();}
template<Index N>
static constexpr std::enable_if_t<
std::is_void<
decltype(
std::declval<TLeafType*>() ->
getOutput<N>(PolyIndex({}))
)
>::value,
Index
> getOutputPortsNumberImpl(void)
{return N;}
template<Index N>
static constexpr std::enable_if_t<
!std::is_void<
decltype(
std::declval<TLeafType*>() ->
getOutput<N>(PolyIndex({}))
)
>::value,
Index
> getOutputPortsNumberImpl(void)
{return getOutputPortsNumberImpl<N + 1>();}
template<Index N>
void getOutputImpl(
PortIdxType<N>, crPolyIndex c_Idx
) const
{throw std::runtime_error("Wrong template argument.");}
};
template<typename T>
class DataflowOutputClass:
public level1<DataflowOutputClass<T>>
{
public:
// if void getOutputImpl(...) const is moved here from level1,
// then the code compiles and works correctly.
//overload for when N = 0
std::unique_ptr<double> getOutputImpl(
PortIdxType<0>, crPolyIndex c_Idx
) const
{
std::unique_ptr<double> mydouble(new double(10));
return mydouble;
}
//overload for when N = 1
std::unique_ptr<int> getOutputImpl(
PortIdxType<1>, crPolyIndex c_Idx
) const
{
std::unique_ptr<int> myint(new int(3));
return myint;
}
};
int main()
{
DataflowOutputClass<int> a;
std::cout << a.getOutputPortsNumber() << std::endl;
}
最佳答案
在原始代码中,我确定了三个问题:
std::declval<TLeafType*>() -> getOutput
尝试在不完整的类中查找名称。std::declval<TLeafType*>() -> getOutput<N>
没有命名功能模板getOutput
。getOutputImpl
声明隐藏与基类名称相同的所有成员函数。std::declval<TLeafType*>() -> getOutput
的返回类型使用表达式
DataflowOutputClass::getOutputPortsNumberImpl
。
level1<DataflowOutputClass<T>>
类中的
DataflowOutputClass
使用CRTP派生时,编译器需要在实例化派生类之前实例化
level1<..>
。因此,在
level1<DataflowOutputClass<T>>
的实例化期间,
DataflowOutputClass<T>
类仍然不完整。
DataflowOutputClass::getOutputPortsNumberImpl
返回类型取决于函数template的template参数来推迟确定:
template<Index N, typename T = TLeafType>
static constexpr std::enable_if_t<
std::is_void<
decltype(
std::declval<T*>() ->
getOutput<N>(PolyIndex({}))
)
>::value,
Index
> getOutputPortsNumberImpl(void)
{return N;}
现在,返回类型取决于函数模板的模板参数。仅在实例化函数时才能解析此返回类型。通过使用
getOutputPortsNumber
中的
main
隐式实例化该函数,其中派生类已完成。
getOutput
,也可以使用默认的
T = level1
。如果使用以下命令,则不会在派生类中查找名称:
template<Index N, typename T = TLeafType>
static constexpr std::enable_if_t<
std::is_void<
decltype(
getOutput<N>(PolyIndex({}))
)
>::value,
Index
> getOutputPortsNumberImpl(void)
{return N;}
但是,要确定此
getOutputPortsNumberImpl
的返回类型,必须实例化
getOutput
的定义,因为
getOutput
使用返回类型推导。其定义将遭受与原始代码类似的问题:它试图查找不完整类型的名称。
declval<TLeafType*> -> getOutput<N>
没有命名功能模板
std::declval<T*>() -> getOutput<N>(PolyIndex({}))
替换了它,但是问题是相同的。考虑:
bool operator> (bool, PolyIndex);
// class template level1
struct derived
{
int getOutput;
};
使用此设置,像
declval<T*>() -> getOutput<N>(PolyIndex({}))
这样的表达式可以解析为:
(
(declval<T*>()->getOutput) < N
)
>
(
PolyIndex({})
)
即
(x < N) > PolyIndex{}
。
getOutput
是模板,请使用
template
关键字:
std::declval<T*>() -> template getOutput<N>(PolyIndex{})
(不需要额外的
()
来初始化
PolyIndex
。)
template<typename T>
class DataflowOutputClass:
public level1<DataflowOutputClass<T>>
{
public:
using level1<DataflowOutputClass<T>>::getOutputImpl;
//overload for when N = 0
std::unique_ptr<double> getOutputImpl(
PortIdxType<0>, crPolyIndex c_Idx
) const;
// ...
};
getOutput<N>(PolyIndex({}))
生成有效的返回类型。当前的方法区分
void
和非
void
返回类型。相反,我们可以简单地检测
getOutput<N>(PolyIndex{})
表达式是否格式正确。为此,我将使用Walter E. Brown的
void_t
技术:
template<typename T>
struct voider { using type = T; };
template<typename T>
using void_if_well_formed = typename voider<T>::type;
我们将按以下方式使用它:
void_if_well_formed< decltype(expression) >
如果表达式格式正确,将产生
void
类型。否则,如果表达式由于立即上下文中的替换失败而格式错误,则整个
void_if_well_formed<..>
将在立即上下文中产生替换失败。可以通过一种称为SFINAE的技术来使用这些类型的错误:替换失败不是错误。在“即时上下文不是错误”中,可以更恰当地将其命名为“替换失败”。
expression<T>()
代表依赖于
T
的任何表达式。
template<typename T, void_if_well_formed<decltype(expression<T>())>* = nullptr>
std::true_type test(std::nullptr_t);
template<typename T>
std::false_type test(void*);
如果现在通过
test<some_type>(nullptr)
调用测试,则首选第一个重载,因为参数类型与函数参数类型完全匹配。但是,第二次过载也是可行的。如果第一个过载由于SFINAE格式错误,则将其从过载集中删除,而选择第二个过载:
template<typename T>
using test_result = decltype( test<T>(nullptr) );
使用这些技术,我们可以实现
level1
,如下所示:
template<typename TLeafType>
class level1
{
public:
template <Index N, typename T = TLeafType>
using output_t =
decltype(std::declval<T*>() ->
getOutputImpl(PortIdxType<N>{}, std::declval<crPolyIndex>()));
static constexpr Natural getOutputPortsNumber(void)
{return getOutputPortsNumberImpl<0>(nullptr);}
template<Index N>
static constexpr Index getOutputPortsNumberImpl(void*)
{return N;}
template<Index N, typename T = TLeafType,
void_if_well_formed<output_t<N, T>>* = nullptr>
static constexpr Index getOutputPortsNumberImpl(std::nullptr_t)
{return getOutputPortsNumberImpl<N + 1>(nullptr);}
};
使用
slightly more work,我们甚至可以这样编写:
template<Index N>
struct HasOutputFor
{
static auto P() -> PortIdxType<N>;
static auto cr() -> crPolyIndex;
template<typename T>
static auto requires_(T&& t) -> decltype(t.getOutputImpl(P(), cr()));
};
template<Index N, typename T = TLeafType, REQUIRE( HasOutputFor<N>(T) )>
static constexpr Index getOutputPortsNumberImpl(std::nullptr_t);
关于c++ - 基于C++ CRTP的数据流输出类设计简化,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/29986291/
我目前正在实现一个通用事件类。事件处理程序有一个发送者参数和可变数量的事件参数。所以事件类的声明如下: template class event; 为了允许某些实现细节,我需要事件的 CRTP,如下所
我需要实现对实现相同接口(interface)的对象 vector 的高效访问。直到现在,我一直在使用带有虚函数的继承:接口(interface)被定义为具有纯虚函数的抽象类,每个对象类都实现了虚函数
在CRTP ,基类可以使用派生类的函数和变量。但是,派生类的类型不能直接被基类使用,见下面代码: #include template class A { public: //using S
我创建了一个模板类,只要发生实例化,它就会触发运行时文本输出: template struct verbose { verbose() { std::cout str
假设我有一个用于矩阵的 CRTP 模板类 template class MatrixBase{ private: //... public: Derived some_function
我已经阅读了很多关于 Curiously Recurring Template Pattern 的帖子而且我仍然不明白为什么我不想只使用模板编程来使用它。 下面是一个从维基百科稍作修改的例子: tem
请帮我解决以下问题: 我有一个类声明为: template class Rim : /* Derive from GenericComponent Design perspective u
有没有办法从 CRTP 基类查询派生类的内容,与 SFINAE 一起使用来启用或禁用基类方法? 我想要完成的可能如下所示: template struct base { struct foo
我有兴趣了解 CRTP。我想为引擎实现一个组件系统,我不想访问组件统一风格 GetComponent("withThisName"); 而是在编译时(虚幻风格) GetComponent(); 虽然实
让我们考虑一个用于打印派生类的 CRTP 模板类 Print: template struct Print { auto print() const -> void; auto se
最近我一直在摆弄模板并偶然发现了以下问题。我正在像这样实现 CRTP 模式: template struct protocol_object { ... }; struct data_obje
我正在尝试在固定大小的缓冲区中构建一条消息,我的图书馆的用户会在其中提供一些数据。我曾经通过给用户一个指向缓冲区的指针并让他们写入它,并通过引用他们写入的字节数来设置一个 size_t 参数来做到这一
我刚刚遇到 CRTP 的问题,我不能在基类和派生类中使用相同的方法名称(具有不同的签名)。重现此问题的示例如下: template struct Base { void foo(){}
考虑以下代码: #include #include #include struct BaseClass { static int identifier() { stati
我有一个纯虚类接口(interface): class Interface { public: virtual ~Interface() noexcept; virtual voi
任何人都可以向我解释为什么 base::blah(string str) 的签名必须是一个字符串而不是引用一个字符串。如果它是一个字符串,编译器会出现以下错误。是因为在模板实例化时编译器拒绝任何隐式转
我遇到了以下代码(存储在 crtp.cc 中)的编译器相关问题: #include #include #include template class AlgebraicVectorExpres
在我的一个项目中,我使用与此处的答案 1 相同的 CRTP 方法(源自 enable_crtp):How do I pass template parameters to a CRTP? 但是我也需要
我目前正在使用 C++ 模板处理 CRTP 模式。在摆弄 visual studio 时,我发现了几种派生类可以调用函数的基类实现的方式/方法。下面是我正在使用的代码以及 3 行注释掉的代码,显示了如
我想创建一个模板类,为类提供通用方法,使其具有成员 m_Type,指定继承类提供的某种类型。考虑一下: template struct TypeAttribute { T m_Type; };
我是一名优秀的程序员,十分优秀!