CRTP:根据导出的 class 内容启用基础 class 中的方法
CRTP: enable methods in base class based on derived class contents
有没有办法从 CRTP 基础 class 查询派生的 class' 内容,以与 SFINAE 一起使用以启用或禁用基础 class 方法?
我想要完成的可能如下所示:
template<typename Derived>
struct base
{
struct foo {};
struct bar {};
void dispatch(int i)
{
switch (i) {
case 0: dispatch(foo{}); break;
case 1: dispatch(bar{}); break;
default: break;
}
}
// catch all for disabled methods
template<typename T> void dispatch(T const&) {}
std::enable_if</* magic that checks if there is in fact Derived::foo(foo) */>
dispatch(foo f)
{
static_cast<Derived*>(this)->foo(f);
}
std::enable_if</* magic that checks if there is in fact Derived::bar(bar) */>
dispatch(bar b)
{
static_cast<Derived*>(this)->bar(b);
}
};
struct derived: public base<derived>
{
// only foo in this one
void foo(foo) { std::cout << "foo()\n"; }
};
只是尝试在 enable_if
中使用 Derived::foo
会导致错误,引用不完整 class(派生)的无效使用。
Is there a way to query derived class' contents from a CRTP base class, to use with SFINAE to enable or disable base class methods?
是的,是的。它遵循一个最小的工作示例:
#include<iostream>
template<typename D>
class base {
template<typename T = D>
auto dispatch(int) -> decltype(std::declval<T>().foo(), void()) {
static_cast<T*>(this)->foo();
}
void dispatch(char) {
std::cout << "base" << std::endl;
}
public:
void dispatch() {
dispatch(0);
}
};
struct derived1: base<derived1> {
void foo() {
std::cout << "derived1" << std::endl;
}
};
struct derived2: base<derived2> {};
int main() {
derived1 d1;
derived2 d2;
d1.dispatch();
d2.dispatch();
}
添加要转发的参数很简单,我希望示例尽可能简单。
在 wandbox 上查看 运行。
正如您从上面的代码片段中看到的,基本思想是使用标记调度和重载方法来启用或禁用基 class 中的方法,并使用派生 class 中的方法] 如果存在的话。
Simply trying to use Derived::foo inside enable_if results in an error citing invalid use of an incomplete class (derived).
那是因为 Derived
当您尝试使用它时实际上是不完整的。标准说:
A class is considered a completely-defined object type (or complete type) at the closing } of the class-specifier.
在你的例子中,派生的 class 有一个基础 class 模板,并且由于显而易见的原因,前者在后者的实例化过程中不是完整的类型。
此外,Derived
不是您的 sfinae 表达式中的实际类型,并且(让我说)sfinae 在这种情况下不起作用 。这就是我在示例中执行以下操作的原因:
template<typename T = D>
auto dispatch(int) -> decltype(std::declval<T>().foo(), void()) {
static_cast<T*>(this)->foo();
}
当然,decltype
这样用也是sfinae的表达方式。如果您愿意,可以使用 std::enable_if_t
做类似的事情。我发现这个版本更容易阅读和理解。
也就是说,您可以通过虚拟方法获得相同的结果。如果您没有充分的理由不这样做,请使用它。
为了完整起见,您的示例已更新为上述技术:
#include<iostream>
template<typename Derived>
struct base
{
struct foo {};
struct bar {};
void dispatch(int i)
{
switch (i) {
case 0: dispatch(0, foo{}); break;
case 1: dispatch(0, bar{}); break;
default: break;
}
}
template<typename T>
void dispatch(char, T const&) {}
template<typename D = Derived>
auto dispatch(int, foo f)
-> decltype(std::declval<D>().foo(f), void())
{
static_cast<D*>(this)->foo(f);
}
template<typename D = Derived>
auto dispatch(int, bar b)
-> decltype(std::declval<D>().bar(b), void())
{
static_cast<D*>(this)->bar(b);
}
};
struct derived: public base<derived>
{
void foo(foo) { std::cout << "foo" << std::endl; }
};
int main() {
derived d;
d.dispatch(0);
d.dispatch(1);
}
在 wandbox 上查看。
有没有办法从 CRTP 基础 class 查询派生的 class' 内容,以与 SFINAE 一起使用以启用或禁用基础 class 方法?
我想要完成的可能如下所示:
template<typename Derived>
struct base
{
struct foo {};
struct bar {};
void dispatch(int i)
{
switch (i) {
case 0: dispatch(foo{}); break;
case 1: dispatch(bar{}); break;
default: break;
}
}
// catch all for disabled methods
template<typename T> void dispatch(T const&) {}
std::enable_if</* magic that checks if there is in fact Derived::foo(foo) */>
dispatch(foo f)
{
static_cast<Derived*>(this)->foo(f);
}
std::enable_if</* magic that checks if there is in fact Derived::bar(bar) */>
dispatch(bar b)
{
static_cast<Derived*>(this)->bar(b);
}
};
struct derived: public base<derived>
{
// only foo in this one
void foo(foo) { std::cout << "foo()\n"; }
};
只是尝试在 enable_if
中使用 Derived::foo
会导致错误,引用不完整 class(派生)的无效使用。
Is there a way to query derived class' contents from a CRTP base class, to use with SFINAE to enable or disable base class methods?
是的,是的。它遵循一个最小的工作示例:
#include<iostream>
template<typename D>
class base {
template<typename T = D>
auto dispatch(int) -> decltype(std::declval<T>().foo(), void()) {
static_cast<T*>(this)->foo();
}
void dispatch(char) {
std::cout << "base" << std::endl;
}
public:
void dispatch() {
dispatch(0);
}
};
struct derived1: base<derived1> {
void foo() {
std::cout << "derived1" << std::endl;
}
};
struct derived2: base<derived2> {};
int main() {
derived1 d1;
derived2 d2;
d1.dispatch();
d2.dispatch();
}
添加要转发的参数很简单,我希望示例尽可能简单。
在 wandbox 上查看 运行。
正如您从上面的代码片段中看到的,基本思想是使用标记调度和重载方法来启用或禁用基 class 中的方法,并使用派生 class 中的方法] 如果存在的话。
Simply trying to use Derived::foo inside enable_if results in an error citing invalid use of an incomplete class (derived).
那是因为 Derived
当您尝试使用它时实际上是不完整的。标准说:
A class is considered a completely-defined object type (or complete type) at the closing } of the class-specifier.
在你的例子中,派生的 class 有一个基础 class 模板,并且由于显而易见的原因,前者在后者的实例化过程中不是完整的类型。
此外,Derived
不是您的 sfinae 表达式中的实际类型,并且(让我说)sfinae 在这种情况下不起作用 。这就是我在示例中执行以下操作的原因:
template<typename T = D>
auto dispatch(int) -> decltype(std::declval<T>().foo(), void()) {
static_cast<T*>(this)->foo();
}
当然,decltype
这样用也是sfinae的表达方式。如果您愿意,可以使用 std::enable_if_t
做类似的事情。我发现这个版本更容易阅读和理解。
也就是说,您可以通过虚拟方法获得相同的结果。如果您没有充分的理由不这样做,请使用它。
为了完整起见,您的示例已更新为上述技术:
#include<iostream>
template<typename Derived>
struct base
{
struct foo {};
struct bar {};
void dispatch(int i)
{
switch (i) {
case 0: dispatch(0, foo{}); break;
case 1: dispatch(0, bar{}); break;
default: break;
}
}
template<typename T>
void dispatch(char, T const&) {}
template<typename D = Derived>
auto dispatch(int, foo f)
-> decltype(std::declval<D>().foo(f), void())
{
static_cast<D*>(this)->foo(f);
}
template<typename D = Derived>
auto dispatch(int, bar b)
-> decltype(std::declval<D>().bar(b), void())
{
static_cast<D*>(this)->bar(b);
}
};
struct derived: public base<derived>
{
void foo(foo) { std::cout << "foo" << std::endl; }
};
int main() {
derived d;
d.dispatch(0);
d.dispatch(1);
}
在 wandbox 上查看。