简体   繁体   English

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

[英]std::get like (partial) template specialization

I have class for complex numbers : 我上复数课:

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

I decided to add function similar to std::get : 我决定添加类似于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;
   }
}

This works OK. 这样就可以了。 Also I know the compiler can optimize it away. 我也知道编译器可以优化它。

Then I decided to rewrite it in different way: 然后,我决定以不同的方式重写它:

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

However this does not compile and I am curious how correct implementation will be? 但是,这无法编译,我很好奇实现的正确性如何?

I tried to check how std::get is made, but I was unable to find anything that was "readable". 我试图检查std::get的制作方式,但是我找不到“可读”的东西。

In C++11 you can implement this exercise like: 在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 is applicable for class templates, not function templates. 部分模板专门化适用于类模板,不适用于功能模板。

In C++14 you can write trivially more concise code using std::enable_if_t . 在C ++ 14中,您可以使用std::enable_if_t编写更加简洁的代码。

And in C++17 you may use if constexpr to write a single function template instead of SFINAE overloads. 在C ++ 17中,您可以使用if constexpr编写单个函数模板,而不是SFINAE重载。

Function templates cannot be partially specialised. 功能模板不能部分专门化。

Another method is tag dispatching that achieves a similar effect using function overloading: 另一种方法是标签分配 ,它使用函数重载可以达到类似的效果:

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

I was able to come up with this, but it looks very complex for such an easy task: 我能够提出这个建议,但是对于这样一个简单的任务来说,它看起来非常复杂:

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

Then I was able to move T into get() method, so I can make less code: 然后,我能够将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);
}

One may "strip" entire template<int I> struct GetHelper class: 可以“剥离”整个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);
}

The problem is that you can't partially specialise function templates. 问题是您不能部分专门化功能模板。 Please read C++ template specialisation of function and Getting “illegal use of explicit template arguments” when doing a pointer partial specialization for a class method , which Nick describes the same solution of. 对类方法进行指针部分专业化时,请阅读函数的C ++模板专业化,以及“非法使用显式模板参数” ,尼克描述了相同的解决方案。

To get the template specialisation to work, please see Nick's answer. 要使模板专业化工作,请参阅Nick的答案。

Your solution does seem fine though, I would just change the switch into an if constexpr . 但是,您的解决方案看起来还不错,我只是将开关更改为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();

}

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM