尝试根据 class 中存在的 typedef 来专门化模板函数

Trying to specialize a template function based on the presence of a typedef within class

我希望能够根据结构中存在的类型来自定义结构的处理(无需为每个自定义结构编写任何额外代码),例如:

struct Normal_t
{
};

struct Custom_t
{
    using my_custom_type = bool;
};

好像我应该可以做这样的事情,但它不起作用:

template <class T, typename Enabler = void>
struct has_custom_type
{
    bool operator()() { return false; }
};

template <class T>
struct has_custom_type<T, typename T::my_custom_type>
{
    bool operator()() { return true; }
};

bool b_normal = has_custom_type<Normal_t>()();  // returns false
bool b_custom = has_custom_type<Custom_t>()();  // returns false, INCORRECT? should return true?

我不明白的是,标准库使用了一些类似但看起来更复杂的类型特征。例如,这有效:

template<bool test, class T = void>
struct my_enable_if
{
};

template<class T>
struct my_enable_if<true, T>
{
    using type = T;
};

template <class T, class Enabler = void>
struct foo
{
    bool operator()() { return false; }
};

template <class T>
struct foo<T, typename my_enable_if<std::is_integral<T>::value>::type>
{
    bool operator()() { return true; }
};

bool foo_float = foo<float>()();    // returns false
bool foo_int = foo<int>()();        // returns true

在这两种情况下,专业化都是基于结构中类型的存在而发生的,在一种情况下 typename T::my_custom_type 而在另一种情况下 typename my_enable_if<std::is_integral<T>::value>::type。为什么第二个版本有效而​​第一个版本无效?

我使用 ... 参数包语法想出了这个解决方法,但我真的很想了解是否有一种方法可以在不使用参数包语法的情况下使用普通模板特化来做到这一点,如果没有,为什么。

template<typename ...Args>                              
bool has_custom_type_2(Args&& ...args)      { return false; }   

template<class T, std::size_t = sizeof(T::my_custom_type)>  
bool has_custom_type_2(T&)                  { return true; }

template<class T, std::size_t = sizeof(T::my_custom_type)>  
bool has_custom_type_2(T&&)                 { return true; }    /* Need this T&& version to handle has_custom_type_2(SomeClass()) where the parameter is an rvalue */

bool b2_normal = has_custom_type_2(Normal_t()); // returns false
bool b2_custom = has_custom_type_2(Custom_t()); // returns true - CORRECT!

问题是您为 Enabler 指定了默认 void 类型,但 T::my_custom_type 不是 void。使用 bool 作为默认类型,或者使用始终 returns void:

std::void_t
template <class T, typename = void>
struct has_custom_type : std::false_type { };

template <class T>
struct has_custom_type<T, std::void_t<typename T::my_custom_type>> : std::true_type { };

This answer 解释了为什么类型应该匹配。

template <class T, typename Enabler = void> // <== void set as default template parameter type
struct has_custom_type
{
    bool operator()() { return false; }
};

template <class T>
struct has_custom_type<T, typename T::my_custom_type>
{
    bool operator()() { return true; }
};

特化在获取模板参数时匹配<T, bool>。但是,当您仅指定 <T>,而没有第二个类型时,它会转到您指定的默认类型 =void 以提出调用 <T, void>,这与您的 bool专业化。

显示它与显式 <T, bool> 匹配的实例:https://godbolt.org/z/MEJvwT

正如其他人所解释的,如果您为第二个模板参数设置 void 默认值,则您的解决方案仅在 my_custom_typevoid 时才有效。

如果my_custom_typebool,您可以设置bool为默认值。但这不是一个很好的解决方案,因为通用性不强。

更一般地说,如果 my_custom_type 不存在但 return 相同类型(void,通常)如果 my_custom_type 存在。

Pre C++17 您可以使用 decltype()std::declval 和逗号运算符的强大功能

template <class T, typename Enabler = void>
struct has_custom_type
 { bool operator()() { return false; } };

template <class T>
struct has_custom_type<T,
   decltype( std::declval<typename T::my_custom_type>(), void() )>
 { bool operator()() { return true; } };

从 C++17 开始,它更简单,因为您可以使用 std::void_t(参见 Evg 的回答,也可以使用 std::true_typestd::false_type 而不是定义 operator()).