问题 是否可以仅通过标识符检查成员模板的存在?


我们可以检测成员吗? function templatevariable templateclass/struct/union template 要么 alias template 不知道数量或性质 template/non-template 参数?

当我试着想到这一点时,没有什么真正想到我的想法。但是让我们有成员函数模板的结构:

struct foo
{
    // Really random. Let's assume we don't know this declaration, just the name "bar"
    template <class T, std::size_t N, class... Args>
    void bar(T a, T b, T(&c)[N], Args const& ...);
};

我如何检查是否 foo::bar 模板存在吗?

基于实例化的类型特征在这里不适用,因为(理论上)我们不知道 我们应该使用哪些参数以什么顺序 和 他们中有多少人。也许一些神奇的查找方法是合适的?或者也许这是不可能的?


搜索时,我找到了 这个问题,但答案中的解决方案需要有关自然的知识 template


这是我的第一个 失败 试图检测 struct template

struct foo
{
    template<class T>
    struct bar { };
};

template <class T, class = void>
struct has_template_bar : std::false_type
{ };

template <class T>
struct has_template_bar <T, void> : std::true_type
{
    template<template<class...> class tplt_tplt = T::bar> // Invalid default argument
    struct placebo
    { };
};

10593
2017-10-01 22:06


起源

如果有两种这样的方法,我想你也希望它返回true? - Aaron McDaid
@AaronMcDaid那太棒了!但我担心增加超载的可能性可能会使事情更加复杂化。 - xinaiz
我确信我几天前就看到了这个答案,这也解决了过载情况。现在找不到它。我会继续寻找! - Aaron McDaid
它(让我说) 不存在 直到你专注它。你想检测它是否存在 bar<void, 0, int>, 举个例子? - skypjack
@AaronMcDaid好吧,我试着简单地考虑一下:)最初的想法是:检查结构是否已声明(可能已经过载,后来出现)成员模板(列出的种类)。你的答案可能是最接近的 - 我的意思是你可以通过他们的名字单独检测模板,这很好。问题是这种方法不区分模板和非模板(在我看来不是一个大问题,基本思想已经完成)。更大的缺点是检查结构不能 final (因为你从中解脱)。 - xinaiz


答案:


我可以向您展示如何检测结构模板:

template < class > struct check_template : std::false_type {};

// Specialize for template classes
template <template<class...> class X, class... Args>
struct check_template< X<Args...> > : std::true_type {};

然后你可以玩 declvalvoid_t等,以检测成员模板。

如果您想要检测类型与元类型,即类似的模板 std::vector 并不是 std::vector<int>,你可以做到以下几点:

#include <iostream>

template <template<class...> class>
constexpr bool is_template()
{
    return true;
}

template <class>
constexpr bool is_template()
{
    return false;
}

struct Foo{};

template<class>
struct TemplateFoo{};

int main()
{
     std::cout << std::boolalpha;
     std::cout << is_template<Foo>() << std::endl;
     std::cout << is_template<TemplateFoo>() << std::endl;
}

住在Coliru

请注意,如果元类型具有任何非类型参数,则解决方案将不起作用

template<class, int> struct X{};

5
2017-10-01 22:15



这两种解决方案都不适用于类似的类型 template<int> struct R {};。 - skypjack
@skypjack那是对的。但是我不知道当你有类型/非类型混合并且你对它们一无所知时怎么能完成这个。 - vsoftco
很公平。我没有说我已经有了这个想法,但它可能是读者可以提到的。就这样。 ;-) - skypjack
@skypjack提到它:) - vsoftco


这种方法在存在多个重载的情况下工作,返回 false_type 当且仅当没有方法或成员被召唤时 bar。它并没有告诉我们什么有用的东西 bar(s)虽然是(稍后更多)。

(注意:这个答案(和问题?)都是愚蠢的。几天前我在SO上学到了这个技术。但是我找不到原来的!)

这用了 void_t,您可能需要自己定义(例如,不是在c ++ 11中):

template<typename ...T>
struct voider { using type = void; };
template<typename ...T>
using void_t = typename voider<T...> :: type;

bar 是我们感兴趣的成员,所以我们用一个名为bar的成员制作一个非常无聊的结构:

struct just_a_bar { int bar; };

然后给出一个模板 T,它声明了一个继承自两者的结构 T 和 just_a_bar

template<typename T>
struct MultipleBars : public T , public just_a_bar { };

现在, decltype(MultipleBars<T>::bar) 当且仅当有成员时,才会出现歧义错误 bar 在T.我们可以使用这个:

template<typename T, typename =void>
struct has_at_least_one_bar : public true_type {};

template<typename T>
struct has_at_least_one_bar<T, void_t< decltype(MultipleBars<T>::bar) >>
    : public false_type { 
};

然后,将以上内容用于实际:

struct zero { };
struct one { 
    void bar(int,int);
};
struct two { 
    //template<typename P, typename Q> // works fine with templates too
    void bar(int);
    void bar(int,int);
};


int main() { 
    cout << boolalpha;
    cout << has_at_least_one_bar<zero>{} << endl; // false
    cout << has_at_least_one_bar<one>{} << endl;  // true
    cout << has_at_least_one_bar<two>{} << endl;  // true
}

一旦你知道 bar 存在,您可能想要更多细节。如果你有一些特定的模式(非模板成员,模板方法只有类型参数,模板方法有两个 int 非类型参数,带有三个模板模板参数的模板方法,...)然后我认为您可以单独测试每个模式。但是最终你可以用有限数量的这种模式检测出什么是有限的。 (事实上​​,你的意思是模板化的方法,而不是模板化的结构,可能会使这更难


4
2017-10-01 22:50



调用时应该是什么模板参数 has_at_least_one_bar?当我尝试 just_a_bar, 然后 MultipleBars 从它那里得到两次。 - xinaiz
我忘了添加一个使用示例。我现在刚刚添加了一些例子。 just_a_bar 是这个特性的实现细节。我们需要一个有“无用”的“虚拟”类型 bar 方法/构件。 - Aaron McDaid
为什么不简单 template<typename ...> using void_t = void;?我认为需要一个中级 struct 是由于标准的缺陷,但现在简单的定义应该工作。 - vsoftco
哦,为了检测底层模板,需要提供 3^n (模板模板,类型和非类型参数)重载(我仍然不确定这些),例如对于最大值。 10个参数,需要59049次重载 - 而这只是一个小的 bar;) - xinaiz
@vsoftco,正确的 void_t。实际上,g ++,clang,MSVC的任何版本都有错误的实现吗?如果没有,那么我很乐意相信它。我不想绊倒一个碰巧使用错误版本的编译器的SO用户 - Aaron McDaid


在C ++ 14中,您可以使用模板变量来检测类型是否为特化:

#include <type_traits>

template<typename>
constexpr bool is_spec = false;

template<template<typename...> class T, typename... U>
constexpr bool is_spec<T<U...>> = true;

struct S {};
template<typename> struct R {};

int main() {
    static_assert(not is_spec<S>, "!");
    static_assert(is_spec<R<void>>, "!");
}

请注意,如果涉及非类型参数,它将不起作用(作为示例 template<int> struct R {};)。


3
2017-10-01 23:00





我想我明白了。谢谢 亚伦麦克戴德 和 vsoftco答案,我成功检测到成员类型模板(alias templatestruct templateclass template 和 union templatemember function templates 有一个额外的缺点,和 member variable templates

这种实现有一些缺点:

  • foo 我们检查名称的存在 bar 不是 final 类型。
  • 将无法检测到具有混合类型/非类型/模板模板参数的模板。
  • 代码有点长。

另外一个缺点是:

  • [注意:很快就会修好!]检查 member function tempalates 将返回 true,如果上课 foo 有任何重载功能 bar。我只是没有办法单独检测过载功能。这也将影响最终 has_member_template 类型特征。

这是实施:

#include <iostream>
#include <type_traits>
#include <iomanip>

/***Check if type is template***/
template <template<class...> class>
constexpr bool is_template_type()
{
    return true;
}

template <class>
constexpr bool is_template_type()
{
    return false;
}

/***Check if T has static member function "bar" ***/
template <class, class = void>
struct has_static_member_function_bar : std::false_type
{ };

template <class T>
struct has_static_member_function_bar<T,
    std::enable_if_t<std::is_function<typename std::remove_pointer<decltype(&T::bar)>::type>::value
        >
    > : std::true_type
{ };

/***Check if T has member function "bar" ***/
template <class, class = void>
struct has_member_function_bar : std::false_type
{ };


template <class T>
struct has_member_function_bar<T,
    std::enable_if_t<std::is_member_function_pointer<decltype(&T::bar)>::value
        >
    > : std::true_type
{ };

/***Check if T has member reference "bar" ***/
template <class, class = void>
struct has_member_reference_bar : std::false_type
{ };

template <class T>
struct has_member_reference_bar<T,
    std::enable_if_t<std::is_reference<decltype(T::bar)>::value
        >
    > : std::true_type
{ };

/***Check if T has static member object "bar" ***/
template <class, class = void>
struct has_static_member_object_bar : std::false_type
{ };

template <class T>
struct has_static_member_object_bar<T,
    std::enable_if_t<std::is_object<typename std::remove_pointer<decltype(&T::bar)>::type>::value &&
                    (!std::is_member_object_pointer<decltype(&T::bar)>::value)

        >
    > : std::true_type
{ };

/***Check if T has member function "bar" ***/
template <class, class = void>
struct has_member_object_bar : std::false_type
{ };

template <class T>
struct has_member_object_bar<T,
    std::enable_if_t<std::is_member_object_pointer<decltype(&T::bar)>::value
        >
    > : std::true_type
{ };

/***Check if T has member alias, struct, class, union template "bar" ***/
template <class, class = void>
struct has_member_type_template_bar : std::false_type
{ };

template <class T>
struct has_member_type_template_bar<T,
    std::enable_if_t<is_template_type<T::template bar>()
        >
    > : std::true_type
{ };

/***Check if T has at least one name "bar" ***/
struct has_at_least_one_bar_impl { int bar; };

template<typename T>
struct bar_overloads : T , has_at_least_one_bar_impl { };

template<typename T, typename = void>
struct has_at_least_one_bar : std::true_type { };

template<typename T>
struct has_at_least_one_bar<T, std::void_t< decltype(bar_overloads<T>::bar) >>
    : std::false_type { };

/***Check if T has member object, reference, not-overloaded function "bar" ***/
template <class, class = void>
struct has_non_type_non_overloaded_member_bar : std::false_type
{ };

template <class T>
struct has_non_type_non_overloaded_member_bar<T,
    std::void_t<decltype((void)(T::bar))>> : std::true_type
{ };


/***Check if T has member function "bar" ***/
template <class, class = void>
struct has_type_member_bar : std::false_type
{ };

template <class T>
struct has_type_member_bar<T,
    std::void_t<typename T::bar>> : std::true_type
{ };

/***Check if T has no more than one member "bar" ***/
template<class, class = void, class = void>
struct has_at_most_one_bar : std::false_type
{ };

template<class T>
struct has_at_most_one_bar<T,
    std::enable_if_t<
        has_type_member_bar<T>::value ||
        has_non_type_non_overloaded_member_bar<T>::value
        >
    > : std::true_type
{ };

/***Check if T has member function template "bar" ***/
template <class, class = void>
struct has_member_function_template_bar : std::false_type
{ };

template <class T>
struct has_member_function_template_bar<T,
    std::enable_if_t<has_at_least_one_bar<T>::value &&
        (!has_member_type_template_bar<T>::value) &&
        (!has_non_type_non_overloaded_member_bar<T>::value) &&
        (!has_member_function_bar<T>::value) &&
        (!has_type_member_bar<T>::value)
        >
    > : std::true_type
{ };

/***Check if T has member variable template "bar" ***/
template <class, class = void>
struct has_member_variable_template_bar : std::false_type
{ };

template <class T>
struct has_member_variable_template_bar<T,
    std::enable_if_t<has_at_least_one_bar<T>::value &&
        (!has_member_type_template_bar<T>::value) &&
        (!has_member_function_template_bar<T>::value) &&
        (!has_type_member_bar<T>::value) &&
        (!has_static_member_function_bar<T>::value) &&
        (!has_member_function_bar<T>::value) &&
        (!has_member_object_bar<T>::value) &&
        (!has_member_reference_bar<T>::value) &&
        (!has_static_member_object_bar<T>::value)>
    > : std::true_type
{ };

/***Check if T has any member template "bar" ***/
template <class, class = void>
struct has_member_template_bar : std::false_type
{ };

template <class T>
struct has_member_template_bar<T,
    std::enable_if_t<has_member_type_template_bar<T>::value ||
        has_member_function_template_bar<T>::value ||
        has_member_variable_template_bar<T>::value>
    > : std::true_type
{ };

实例

输出示例:

---Has type template bar---
consists_no_bar:                  false
consists_alias:                   false
consists_struct:                  false
consists_class:                   false
consists_union:                   false
consists_variable:                false
consists_function:                false
consists_overloaded_func:         false
consists_reference:               false
consists_t_alias:                 true
consists_t_struct:                true
consists_t_class:                 true
consists_t_union:                 true
consists_t_variable:              false
consists_t_function:              false
consists_t_overloaded_function:   false
consists_s_variable:              false
consists_s_function:              false
consists_s_overloaded_func:       false
consists_s_t_function:            false
consists_s_t_overloaded_function: false

--Has member function template bar---
consists_no_bar:                  false
consists_alias:                   false
consists_struct:                  false
consists_class:                   false
consists_union:                   false
consists_variable:                false
consists_function:                false
consists_overloaded_func:         true // implmementation bug
consists_reference:               false
consists_t_alias:                 false
consists_t_struct:                false
consists_t_class:                 false
consists_t_union:                 false
consists_t_variable:              false
consists_t_function:              true
consists_t_overloaded_function:   true
consists_s_variable:              false
consists_s_function:              false
consists_s_overloaded_func:       true // implmementation bug
consists_s_t_function:            true
consists_s_t_overloaded_function: true

--Has member variable template bar---
consists_no_bar:                  false
consists_alias:                   false
consists_struct:                  false
consists_class:                   false
consists_union:                   false
consists_variable:                false
consists_function:                false
consists_overloaded_func:         false
consists_reference:               false
consists_t_alias:                 false
consists_t_struct:                false
consists_t_class:                 false
consists_t_union:                 false
consists_t_variable:              true
consists_t_function:              false
consists_t_overloaded_function:   false
consists_s_variable:              false
consists_s_function:              false
consists_s_overloaded_func:       false
consists_s_t_function:            false
consists_s_t_overloaded_function: false

--Has any member template bar---
consists_no_bar:                  false
consists_alias:                   false
consists_struct:                  false
consists_class:                   false
consists_union:                   false
consists_variable:                false
consists_function:                false
consists_overloaded_func:         true // implmementation bug
consists_reference:               false
consists_t_alias:                 true
consists_t_struct:                true
consists_t_class:                 true
consists_t_union:                 true
consists_t_variable:              true
consists_t_function:              true
consists_t_overloaded_function:   true
consists_s_variable:              false
consists_s_function:              false
consists_s_overloaded_func:       true // implmementation bug
consists_s_t_function:            true
consists_s_t_overloaded_function: true

我仍然很难过,我无法检测到过载的功能......但它很有趣:)


1
2017-10-02 19:16