【问题标题】:std::get like (partial) template specializationstd::get 像(部分)模板专业化
【发布时间】:2018-08-28 08:55:36
【问题描述】:

我有复数课程

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 是如何制作的,但我找不到任何“可读”的内容。

【问题讨论】:

  • 在哪种情况下get&lt;0&gt;(myComplex)myComplex.r 更可取?
  • 比较“标准”,以后做Quaternion类,就可以使用统一接口了。也是教育原因。
  • 如果std::get 与您的班级合作,那将更加标准。

标签: c++ c++11 templates


【解决方案1】:

在 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 过载。

【讨论】:

    【解决方案2】:

    函数模板不能部分特化。

    另一种方法是标签调度,它使用函数重载实现了类似的效果:

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

    【讨论】:

    • 这是我最喜欢的方式,我怎么忘记了。
    【解决方案3】:

    我能够想出这个,但是对于这么简单的任务来说它看起来非常复杂:

    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&lt;int I&gt; 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);
    }
    

    【讨论】:

      【解决方案4】:

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

      要使模板专业化发挥作用,请参阅 Nick 的回答。

      不过,您的解决方案确实不错, 我只需将开关更改为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();
      
      }
      

      【讨论】:

        猜你喜欢
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 2011-04-12
        • 2015-08-21
        相关资源
        最近更新 更多