std :: get like(部分)模板专业化

时间:2018-08-28 08:55:36

标签: c++ c++11 templates

我上了复数类:

template<typename T>
struct Complex{
    T r;
    T i;
};

我决定添加类似于std::get的功能:

template<int X, typename T>
T get(const Complex<T> &a){
   switch(X){
       case 0: return a.r;
       case 1: return a.i;
   }
}

这正常。我也知道编译器可以优化它。

然后我决定以其他方式重写它:

template<int X,typename T>
T get(const Complex<T> &a);

template<typename T>
constexpr T get<0, T>(const Complex<T> &a){
    return a.r;
}

template<typename T>
constexpr T get<1, T>(const Complex<T> &a){
    return a.i;
}

但是这无法编译,我很好奇实现的正确性如何?

我试图检查std::get的制作方法,但找不到任何“可读”的东西。

4 个答案:

答案 0 :(得分:3)

在C ++ 11中,您可以像这样实现此练习:

#include <type_traits>

template<typename T>
struct Complex{
    T r;
    T i;
};


template<int X, typename T>
constexpr typename std::enable_if<X == 0,T>::type
get(const Complex<T> &a){
    return a.r;
}

template<int X, typename T>
constexpr typename std::enable_if<X == 1,T>::type
get(const Complex<T> &a){
    return a.i;
}

Live demo

Partial template specialization适用于 类模板,而不是功能模板。

在C ++ 14中,您可以使用std::enable_if_t编写更加简洁的代码。

在C ++ 17中,您可以使用if constexpr来编写单个函数模板,而不是 SFINAE重载。

答案 1 :(得分:1)

功能模板不能部分专门化。

另一种方法是 tag dispatch ,它使用函数重载达到类似的效果:

template<int X>
using Int = std::integral_constant<int, X>;

template<typename T> inline T get(const Complex<T> &a, Int<0>) { return a.r; }
template<typename T> inline T get(const Complex<T> &a, Int<1>) { return a.i; }

template<int X, typename T>
inline T get(const Complex<T> &a) { return get(a, Int<X>{}); }

答案 2 :(得分:0)

我能够解决这个问题,但是对于这样一个简单的任务来说,它看起来非常复杂:

namespace complex_impl__{
    template<int X, typename T>
    struct GetHelper{
        static T get(const Complex<T> &a);
    };

    template<typename T>
    struct GetHelper<0, T>{
        constexpr static T get(const Complex<T> &a){
            return a.r;
        }
    };

    template<typename T>
    struct GetHelper<1, T>{
        constexpr static T get(const Complex<T> &a){
            return a.i;
        }
    };
}

template<int I,typename T>
constexpr T get(const Complex<T> &a){
    return complex_impl__::GetHelper<I, T>::get(a);
}

然后,我能够将T移至get()方法中,因此可以减少代码量:

namespace complex_impl__{
    template<int I>
    struct GetHelper{
        template<typename T>
        static T get(const Complex<T> &a);
    };

    template<>
    struct GetHelper<0>{
        template<typename T>
        constexpr static T get(const Complex<T> &a){
            return a.r;
        }
    };

    template<>
    struct GetHelper<1>{
        template<typename T>
        constexpr static T get(const Complex<T> &a){
            return a.i;
        }
    };
}

template<int I,typename T>
constexpr T get(const Complex<T> &a){
    return complex_impl__::GetHelper<I>::get(a);
}

一个人可以“剥离”整个template<int I> struct GetHelper类:

namespace complex_impl__{
    template<int I>
    struct GetHelper;

    template<>
    struct GetHelper<0>{
        template<typename T>
        constexpr static T get(const Complex<T> &a){
            return a.r;
        }
    };

    template<>
    struct GetHelper<1>{
        template<typename T>
        constexpr static T get(const Complex<T> &a){
            return a.i;
        }
    };
}

template<int I,typename T>
constexpr T get(const Complex<T> &a){
    return complex_impl__::GetHelper<I>::get(a);
}

答案 3 :(得分:0)

问题是您不能对功能模板进行部分专业化。请阅读 C++ template specialisation of functionGetting “illegal use of explicit template arguments” when doing a pointer partial specialization for a class method,尼克描述了相同的解决方案。

要使模板专业化工作,请参阅尼克的答案。

您的解决方案看起来确实不错, 我只是将开关更改为if constexpr

#include <iostream>

template<typename T>
struct Complex 
{
    T r;
    T i;
};

template<int X, typename T>
constexpr T get(const Complex<T> &a)
{
    if constexpr(X == 0)
    {
        return a.r;
    }
    else if constexpr (X == 1)
    {
        return a.i;
    }
}

int main()
{
    Complex<int> test;
    test.r = 1;
    test.i = 12;

    std::cout << get<0>(test) << std::endl;
    std::cout << get<1>(test) << std::endl;

    std::cin.get();

}