Bru*_*uno 19 c++ sfinae enable-if c++11
使用C++ 11 enable_if我想为函数定义几个专门的实现(比如参数的类型)以及默认实现.定义它的正确方法是什么?
以下示例无法按预期工作,因为无论何种类型调用"通用"实现T.
#include <iostream>
template<typename T, typename Enable = void>
void dummy(T t)
{
  std::cout << "Generic: " << t << std::endl;
}
template<typename T, typename std::enable_if<std::is_integral<T>::value>::type>
void dummy(T t)
{
  std::cout << "Integral: " << t << std::endl;
}
template<typename T, typename std::enable_if<std::is_floating_point<T>::value>::type>
void dummy(T t)
{
  std::cout << "Floating point: " << t << std::endl;
}
int main() {
  dummy(5); // Print "Generic: 5"
  dummy(5.); // Print "Generic: 5"
}
我的最小示例中的一个解决方案在于明确地将"通用"实现声明为不使用整数或浮点类型
std::enable_if<!std::is_integral<T>::value && !std::is_floating_point<T>::value>::type
这正是我想要避免的,因为在我的实际用例中有很多专门的实现,我想避免一个非常长的(容易出错!)条件的默认实现.
W.F*_*.F. 14
功能不能部分专业化.我假设你想要做的是更喜欢那些包含显式条件的重载?实现这一点的一种方法是在default函数声明中使用variadic参数省略号,因为省略号函数在重载分辨率顺序中具有较低的优先级:
#include <iostream>
template<typename T>
void dummy_impl(T t, ...)
{
  std::cout << "Generic: " << t << std::endl;
}
template<typename T, typename std::enable_if<std::is_integral<T>::value>::type* = nullptr>
void dummy_impl(T t, int)
{
  std::cout << "Integral: " << t << std::endl;
}
template<typename T, typename std::enable_if<std::is_floating_point<T>::value>::type* = nullptr>
void dummy_impl(T t, int)
{
  std::cout << "Floating point: " << t << std::endl;
}
template <class T>
void dummy(T t) {
   dummy_impl(t, int{});
}
int main() {
  dummy(5); 
  dummy(5.); 
  dummy("abc"); 
}
输出:
Integral: 5
Floating point: 5
Generic: abc
注释中提到的另一个选项@doublep是通过使用带有函数实现的结构,然后对其进行部分特化.
Vit*_*meo 13
您可以引入a rank来优先处理一些重载:
template <unsigned int N>
struct rank : rank<N - 1> { };
template <>
struct rank<0> { };
然后,您可以dummy像这样定义重载:
template<typename T>
void dummy(T t, rank<0>)
{
    std::cout << "Generic: " << t << std::endl;
}
template<typename T, 
         typename std::enable_if<std::is_integral<T>::value>::type* = nullptr>
void dummy(T t, rank<1>)
{
    std::cout << "Integral: " << t << std::endl;
}
template<typename T, 
         typename std::enable_if<std::is_floating_point<T>::value>::type* = nullptr>
void dummy(T t, rank<1>)
{
    std::cout << "Floating point: " << t << std::endl;
}
然后,您可以隐藏后面的呼叫dispatch:
template <typename T>
void dispatch(T t)
{
   return dummy(t, rank<1>{});
}
用法:
int main() 
{
    dispatch(5);    // Print "Integral: 5"
    dispatch(5.);   // Print "Floating point: 5"
    dispatch("hi"); // Print "Generic: hi"
}
说明:
使用rank引入"优先级",因为需要隐式转换才能将a转换rank<X>为rank<Y>when X > Y.dispatch首先尝试调用dummy用rank<1>,你限制超载给予优先考虑.如果enable_if失败,rank<1>则隐式转换为rank<0>并进入"后备"案例.
额外奖励:这是使用的C++ 17实现if constexpr(...).
template<typename T>
void dummy(T t)
{
    if constexpr(std::is_integral_v<T>)
    {
        std::cout << "Integral: " << t << std::endl;
    }
    else if constexpr(std::is_floating_point_v<T>)
    {
        std::cout << "Floating point: " << t << std::endl;
    }
    else
    {
        std::cout << "Generic: " << t << std::endl;
    }
}
我会像这样使用标签调度:
namespace Details
{
    namespace SupportedTypes
    {
        struct Integral {};
        struct FloatingPoint {};
        struct Generic {};
    };
    template <typename T, typename = void>
    struct GetSupportedType
    {
        typedef SupportedTypes::Generic Type;
    };
    template <typename T>
    struct GetSupportedType< T, typename std::enable_if< std::is_integral< T >::value >::type >
    {
        typedef SupportedTypes::Integral Type;
    };
    template <typename T>
    struct GetSupportedType< T, typename std::enable_if< std::is_floating_point< T >::value >::type >
    {
        typedef SupportedTypes::FloatingPoint Type;
    };
    template <typename T>
    void dummy(T t, SupportedTypes::Generic)
    {
        std::cout << "Generic: " << t << std::endl;
    }
    template <typename T>
    void dummy(T t, SupportedTypes::Integral)
    {
        std::cout << "Integral: " << t << std::endl;
    }
    template <typename T>
    void dummy(T t, SupportedTypes::FloatingPoint)
    {
        std::cout << "Floating point: " << t << std::endl;
    }
} // namespace Details
然后像这样隐藏锅炉板代码:
template <typename T>
void dummy(T t)
{
    typedef typename Details::GetSupportedType< T >::Type SupportedType;
    Details::dummy(t, SupportedType());
}
GetSupportedType 为您提供了一种猜测您将要使用的实际类型的中心方法,即每次添加新类型时您想要专门化的类型.
然后,您只需dummy通过提供正确标记的实例来调用正确的重载.
最后,调用dummy:
dummy(5); // Print "Generic: 5"
dummy(5.); // Print "Floating point: 5"
dummy("lol"); // Print "Generic: lol"