我有这样的类型:
templatestruct wrapper { using foo = typename T::foo; using bar = typename T::bar; using baz = typename T::baz; // More of those... };
我想foo
,bar
,baz
和当且仅当在同等类型的存在来定义同等类型的别名T
.使用std::conditional
allow的解决方案当它不存在时用其他东西替换它,但我不知道如果模板类型中不存在相应的类型,它根本不存在.wrapper
如果T
没有定义其中一个类型别名,则上面的代码在实例化时会导致错误.
我不能wrapper
继承,T
因为wrapper
不应该做所有事情都T
可以.此外,使用部分专业化将导致某种指数爆炸,并很快变得不可维护.我大概可以做foo
,bar
...模板类型别名注入一个std::enable_if
在默认模板参数,但随后用户会写wrapper
,wrapper
而不是wrapper
,wrapper
等...我不希望出现这种情况.
是否有一种简单但可维护的方法来定义这样的类型别名只有在相应的类型别名存在时T
?
你可以定义check_foo
,check_bar
和check_baz
特征,其只拥有类型,如果它存在,那么在所有这些继承wrapper
:
templatestruct check_foo{}; template struct check_foo > { using foo = typename T::foo; }; // ditto for bar, baz, etc. template struct wrapper : check_foo , check_bar , check_baz { };
它是每种类型的一个额外结构,但肯定比你提到的指数版本更好.如果你恰当地反常,你甚至可以把它变成一个宏:
#define DEFINE_CHECKER(NAME) \ templatestruct check_##NAME{}; \ template struct check_##NAME > \ { using NAME = typename T::NAME; }; DEFINE_CHECKER(foo) DEFINE_CHECKER(bar) DEFINE_CHECKER(baz)
可怕,我知道,但我认为你可能需要支付这个价格,如果你真的想要wrapper
而不是wrapper
.如果您使用宏版本,添加新类型将只是一个新的DEFINE_CHECKER(newname)
并添加check_newname
到包装器继承列表.可能更糟.
Live Demo
请注意void_t
@TartanLlama 使用的答案很好.但是,在C++ 17中,很可能会有一些标准库帮助程序,例如is_detected_v
那些可以进行调用的标准库帮助程序void_t
.
#include// helpers to reduce boilerplate template struct empty_base {}; template class Holder, template class Op, class Arg> using inject_or_t = std::conditional_t < std::experimental::is_detected_v , Holder , empty_base > >; // add detector + holder for every conditional nested type template using foo_t = typename T::foo; template struct foo_holder { using foo = foo_t ; }; template using bar_t = typename T::bar; template struct bar_holder { using bar = bar_t ; }; template using baz_t = typename T::baz; template struct baz_holder { using baz = baz_t ; }; // wrapper is now simply: template struct wrapper : inject_or_t , inject_or_t , inject_or_t {}; struct Test { using foo = int; using bar = int; using baz = int; }; int main() { static_assert(!std::experimental::is_detected_v >); static_assert(!std::experimental::is_detected_v >); static_assert(!std::experimental::is_detected_v >); static_assert(std::experimental::is_detected_v >); static_assert(std::experimental::is_detected_v >); static_assert(std::experimental::is_detected_v >); }
活生生的例子说明,他是非常罕见的例子,其中的libstdc ++ 6.0 SVN主干可以(现在!)做在libc ++ 3.9 SVN主干可以不是一个.
这需要为每个要注入的类型添加检测器别名和持有者结构,并且完全不需要宏包装器.