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

时间:2016-10-01 22:06:36

标签: c++ templates c++14 sfinae typetraits

我们是否可以在不知道{的数量或性质的情况下检测成员function templatevariable templateclass / struct / union templatealias template {1}} / 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& ...); }; 模板是否存在?

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

搜索时,我找到this question,但答案中的解决方案需要了解foo::bar的性质。

以下是我第一次失败尝试检测template

struct template

4 个答案:

答案 0 :(得分:5)

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

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;
}

Live on Coliru

请注意,如果元类型具有任何非类型参数(例如

),则解决方案将不起作用
template<class, int> struct X{};

答案 1 :(得分:4)

这种方法在存在多个重载的情况下工作,当且仅当没有方法或名为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的模板,它声明了一个继承自Tjust_a_bar的结构。

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

现在,只有当T中有成员decltype(MultipleBars<T>::bar)时,bar才会出现歧义错误。我们可以使用此代码:

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非类型参数的模板方法,带有三个模板模板参数的模板方法,...)然后我认为你可以单独测试每种模式。但是最终你可以用有限数量的这种模式检测到的东西是有限的。 (事实上​​,你的意思是模板化的方法,而不是模板化的结构,可能会使这更加困难

答案 2 :(得分:3)

在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 :(得分:1)

我想我明白了。感谢Aaron McDaidvsoftco个答案,我成功检测到了成员类型模板(alias templatestruct templateclass templateunion template),{{ 1}}还有一个额外的缺点,member function templates

这种实现有一些缺点:

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

另外一个缺点是:

  • [注意:即将修复!]如果班级final有任何重载函数member function tempalates,则true将会返回foo。我只是没有办法单独检测过载功能。这也会影响最终的bar类型特征。

以下是实施:

has_member_template

Live example

示例输出:

#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
{ };

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