【问题标题】:function implementation with enable_if outside of class definitionenable_if 在类定义之外的函数实现
【发布时间】:2018-01-27 20:56:09
【问题描述】:

所以基本上,我现在有一个非常基本的泛型类,目前正在测试 type_traits 标头。我目前正在尝试制作一个函数来处理某些类型,即现在的算术类型。

#include <type_traits>

template <typename T> class Test {
    public:
        template <typename U = T> 
        typename std::enable_if<std::is_arithmetic<U>::value>::type print();
}; 

该函数完美运行,仅适用于算术类型。

但我喜欢让我的类保持整洁,只让它们有原型,而函数实现在类之外。

使用标准模板,即

void test();

template <typename T> void Test<T>::test() {}

这很简单,我知道怎么做,但是我不知道如何使用“std::enable_if”在类之外声明实现,并且我在编译期间所做的每一次尝试都表明原型不匹配任何在课堂里。

我在这里找到了类似的question,但是那里的课程是标准的而不是通用的。

PS。我正在使用 MinGW-w64 和 -std=c++17

【问题讨论】:

    标签: c++ templates sfinae typetraits c++17


    【解决方案1】:

    类模板需要一组模板参数,成员函数模板需要一组单独的模板参数。您需要重复整个复杂的返回类型,因为它是函数模板签名的一部分。请注意,您不能重复默认参数=T,否则编译器会认为您尝试定义它两次(不检查新定义是否相同)。

    template <typename T> template <typename U>
    typename std::enable_if<std::is_arithmetic<U>::value>::type
    Test<T>::print()
    {
        // Implementation here.
    }
    

    顺便说一句,您正在使用“漫长的方式”来编写类型,这在 C++11 中是必需的。但是 C++14 引入了 std::enable_if_t 快捷方式,而 C++17 引入了 std::is_arithmetic_v 快​​捷方式。所以如果你用的是C++17,你也可以写类型

    typename std::enable_if<std::is_arithmetic<U>::value>::type
    

    就像

    std::enable_if_t<std::is_arithmetic_v<U>>
    

    【讨论】:

      【解决方案2】:

      如果您将enable_if 放在默认模板参数中,无论如何 imo 更好,类外定义会变得容易一些:

      template<typename T>
      struct Test
      {
          template <typename S = T
                  , typename = typename std::enable_if<std::is_arithmetic<S>::value>::type >
          void print();
      };
      
      template<typename T>
      template<typename S, typename>
      void Test<T>::print()
      {
          //some code
      }
      

      【讨论】:

        【解决方案3】:

        你可以试试

        template <typename T>
        template <typename U>
        std::enable_if_t<std::is_arithmetic<U>::value> Test<T>::print()
         { /* do something */ }
        

        以下是一个完整的工作示例

        #include <iostream>
        #include <type_traits>
        
        template <typename T> class Test
         {
           public:
              template <typename U = T>
              std::enable_if_t<std::is_arithmetic<U>::value> print();
         }; 
        
        template <typename T>
        template <typename U>
        std::enable_if_t<std::is_arithmetic<U>::value> Test<T>::print()
         { std::cout << "test!" << std::endl; }
        
        int main ()
         {
           Test<int>  ti;
           Test<void> tv;
        
           ti.print();   // compile
           //tv.print(); // compilation error
         }
        

        离题 1

        请注意,您的解决方案可能会以这种方式被劫持

        Test<void>{}.print<int>(); 
        

        为避免此问题,您可以强制 T 等于 U

        template <typename T> class Test
         {
           public:
              template <typename U = T>
              std::enable_if_t<   std::is_arithmetic<U>::value
                               && std::is_same<T, U>::value> print()
               { }
         }; 
        

        离题 2

        如您所见,您必须重复 SFINAE 部分(std::enable_if_tstd::is_arithmeticstd::is_same)。

        考虑到您必须在标题中重复实现,我认为(恕我直言)在类主体之外编写模板类的实现并不是一个好主意。

        【讨论】:

        • 我明白了,感谢您提出的离题建议,从没想过它可能会以这种方式被劫持。对于外部实现,我的 IDE (eclipse) 有一个很好的功能 ctrl+click 导航到实现,所以我使用这个类就像一本书的索引,但我同意长模板这是一个麻烦
        【解决方案4】:

        由于您尚未发布您的尝试,我无法告诉您哪里出错了。但这是在类定义之外实现成员函数的方式(虽然它仍然需要是implemented in the header,所以我认为这不值得麻烦)

        template <typename T> class Test {
            public:
                template <typename U = T>
                typename std::enable_if<std::is_arithmetic<U>::value>::type print();
        };
        
        template <typename T>  // class template parameter
        template <typename U>  // function template parameter
        inline typename std::enable_if<std::is_arithmetic<U>::value>::type Test<T>::print()
        {
        }
        

        Live demo

        【讨论】:

          【解决方案5】:
          template<typename T>
          struct test
          {
              template<typename U = T>
              typename std::enable_if<std::is_arithmetic<U>::value>::type print();
          };
          template<typename T> template<typename U>
          typename std::enable_if<std::is_arithmetic<U>::value>::type test<T>::print()
          {
          }
          void foo()
          {
              test<int> t;
              t.print();
              test<void*> u;
              u.print();
          }
          

          【讨论】:

            【解决方案6】:

            如果您需要额外的模板参数 U,正如其他答案所解释的,正确的语法是

            template<typename T>
            struct test
            {
                template<typename U>
                ... a_method(...);
            };
            
            template<typename T> 
            template<typename U>
            ... test<T>::a_method(...)
            {
               ...
            }
            

            但是,在您的特殊情况下,如果您只需要检查 T 类型的某些属性,这确实是一个额外的复杂问题。 U 类型的介绍是“人造的”,只是因为 SFINAE 而出现在这里

            恕我直言,使用if constexpr 更加优雅和简单

            #include <iostream>
            #include <type_traits>
            
            template <typename T>
            class Test
            {
             public:
              void print();
            };
            
            template <typename T>
            void Test<T>::print()
            {
              if constexpr (std::is_arithmetic_v<T>)
              {
                std::cout << "\nOk T is arithmetic";
                // ... your implementation here ...
              }
              else
              {
                // throw an exception or do what ever you want,
                // here a compile-time error
                static_assert(!std::is_same_v<T, T>, "not implemented yet...");
              }
            }
            main()
            {
              Test<int> t;
              t.print();
            
              Test<void> t2;
              // t2.print(); <- will generate a compile time error
            }
            

            【讨论】:

              猜你喜欢
              • 1970-01-01
              • 1970-01-01
              • 1970-01-01
              • 1970-01-01
              • 1970-01-01
              • 1970-01-01
              • 1970-01-01
              • 2020-05-30
              • 1970-01-01
              相关资源
              最近更新 更多