条件类型别名定义

Mor*_*enn 9 c++ templates sfinae template-meta-programming c++14

我有这样的类型:

template<typename T>
struct wrapper
{
    using foo = typename T::foo;
    using bar = typename T::bar;
    using baz = typename T::baz;
    // More of those...
};
Run Code Online (Sandbox Code Playgroud)

我想foo,bar,baz和当且仅当在同等类型的存在来定义同等类型的别名T.使用std::conditionalallow的解决方案当它不存在时用其他东西替换它,但我不知道如果模板类型中不存在相应的类型,它根本不存在.wrapper<T>如果T没有定义其中一个类型别名,则上面的代码在实例化时会导致错误.

我不能wrapper继承,T因为wrapper不应该做所有事情都T可以.此外,使用部分专业化将导致某种指数爆炸,并很快变得不可维护.我大概可以做foo,bar...模板类型别名注入一个std::enable_if在默认模板参数,但随后用户会写wrapper<T>::foo<>,wrapper<T>::bar<>而不是wrapper<T>::foo,wrapper<T>::bar等...我不希望出现这种情况.

是否有一种简单但可维护的方法来定义这样的类型别名只有在相应的类型别名存在时T

Tar*_*ama 9

你可以定义check_foo,check_barcheck_baz特征,其只拥有类型,如果它存在,那么在所有这些继承wrapper:

template <typename T, typename=void> 
struct check_foo{};

template <typename T> 
struct check_foo<T, void_t<typename T::foo>> { 
    using foo = typename T::foo; 
};

// ditto for bar, baz, etc.

template <typename T>
struct wrapper :
    check_foo<T>,
    check_bar<T>,
    check_baz<T>
{ };
Run Code Online (Sandbox Code Playgroud)

它是每种类型的一个额外结构,但肯定比你提到的指数版本更好.如果你恰当地反常,你甚至可以把它变成一个宏:

#define DEFINE_CHECKER(NAME) \
    template <typename T, typename=void> struct check_##NAME{}; \
    template <typename T> struct check_##NAME<T,void_t<typename T::NAME>> \
    { using NAME = typename T::NAME; };

DEFINE_CHECKER(foo)
DEFINE_CHECKER(bar)
DEFINE_CHECKER(baz)
Run Code Online (Sandbox Code Playgroud)

可怕,我知道,但我认为你可能需要支付这个价格,如果你真的想要wrapper<T>::bar而不是wrapper<T>::bar<>.如果您使用宏版本,添加新类型将只是一个新的DEFINE_CHECKER(newname)并添加check_newname<T>到包装器继承列表.可能更糟.

Live Demo


Tem*_*Rex 5

请注意void_t@TartanLlama 使用的答案很好.但是,在C++ 17中,很可能会有一些标准库帮助程序,例如is_detected_v那些可以进行调用的标准库帮助程序void_t.

#include <experimental/type_traits>

// helpers to reduce boilerplate
template<class Tag>
struct empty_base {};

template<template<class> class Holder, template<class> class Op, class Arg>
using inject_or_t = std::conditional_t
<
    std::experimental::is_detected_v<Op, Arg>,
    Holder<Arg>,
    empty_base<Op<Arg>>
>;

// add detector + holder for every conditional nested type

template<class T>
using foo_t = typename T::foo;

template<class T>
struct foo_holder { using foo = foo_t<T>; };

template<class T>
using bar_t = typename T::bar;

template<class T>
struct bar_holder { using bar = bar_t<T>; };

template<class T>
using baz_t = typename T::baz;

template<class T>
struct baz_holder { using baz = baz_t<T>; };

// wrapper is now simply:

template<class T>
struct wrapper
:   inject_or_t<foo_holder, foo_t, T>
,   inject_or_t<bar_holder, bar_t, T>
,   inject_or_t<baz_holder, baz_t, T>
{};

struct Test
{
    using foo = int;
    using bar = int;
    using baz = int;
};

int main()
{
    static_assert(!std::experimental::is_detected_v<foo_t, wrapper<int>>);
    static_assert(!std::experimental::is_detected_v<bar_t, wrapper<int>>);
    static_assert(!std::experimental::is_detected_v<baz_t, wrapper<int>>);

    static_assert(std::experimental::is_detected_v<foo_t, wrapper<Test>>);
    static_assert(std::experimental::is_detected_v<bar_t, wrapper<Test>>);
    static_assert(std::experimental::is_detected_v<baz_t, wrapper<Test>>);
}
Run Code Online (Sandbox Code Playgroud)

活生生的例子说明,他是非常罕见的例子,其中的libstdc ++ 6.0 SVN主干可以(现在!)做在libc ++ 3.9 SVN主干可以不是一个.

这需要为每个要注入的类型添加检测器别名和持有者结构,并且完全不需要宏包装器.