【问题标题】:Class template specialization within template class模板类中的类模板特化
【发布时间】:2016-10-16 07:47:31
【问题描述】:

重构遗留代码我想合并彼此相关的单独模板类/结构(以避免命名空间污染)。

Nested(下)是MyStruct 的辅助类,我想将其移入MyStruct。

但我无法完成这项工作:

#include <type_traits>
#include <iostream>

struct YES {} ;
struct NO {};

template <typename TYPE>
struct MyStruct
{

    template <typename TYPE_AGAIN = TYPE, typename SELECTOR = NO>
    struct Nested
    {
        static void Print(void)
        {
            std::cout << "MyStruct::Nested<bool = false>::Print()" << std::endl;
        }
    };


    template <>
    struct Nested<TYPE, typename std::enable_if<std::is_integral<TYPE>::value, YES>::type>
    {
        static void Print(void)
        {
           std::cout << "MyStruct::Nested<bool = true>::Print()" << std::endl;
        }
    }; 

};

编译器抱怨:

g++ -O0 -g3 -Wall -c -fmessage-length=0 -std=c++11 -MMD -MP -MF"main.d" -MT"main.d" -o "main.o" "../main.cpp"
In file included from ../main.cpp:8:0:
../MyStruct.h:31:12: error: explicit specialization in non-namespace scope ‘struct MyStruct<TYPE>’
  template <>
            ^
make: *** [main.o] Error 1

实际上,我也很困扰必须包括

<typename TYPE_AGAIN = TYPE>

但没有,还有更多的抱怨:

g++ -O0 -g3 -Wall -c -fmessage-length=0 -std=c++11 -MMD -MP -MF"main.d" -MT"main.d" -o "main.o" "../main.cpp"
In file included from ../main.cpp:8:0:
../MyStruct.h:31:12: error: explicit specialization in non-namespace scope ‘struct MyStruct<TYPE>’
  template <>
            ^
../MyStruct.h:32:9: error: template parameters not used in partial specialization:
  struct Nested<typename std::enable_if<std::is_integral<TYPE>::value, YES>::type>
     ^
../MyStruct.h:32:9: error:         ‘TYPE’
make: *** [main.o] Error 1

【问题讨论】:

    标签: c++ c++11 templates sfinae


    【解决方案1】:

    您不能在非命名空间范围内专门化模板,例如 struct 在您的情况下。

    您必须将特化放在结构定义之外:

    template<typename TYPE> template<>
    struct MyStruct<TYPE>::Nested<...> {};
    

    但是现在你有另一个问题,如果你想在模板类中专门化一个模板,你必须为每个模板类专门化它。你不能只专门化一个成员函数,你必须专门化整个类。

    所以,你需要这样做:

    template<> template<>
    struct MyStruct<int>::Nested<...> {};
    

    另外,你真的不需要 SFINAE:

    template<typename SELECTOR>
    struct Nested; // Default invalid SELECTOR
    
    template<>
    struct Nested<YES> { /*...*/ };
    
    template<>
    struct Nested<NO> { /*...*/ };
    

    【讨论】:

    • 非常感谢! - 这个模板 模板 语法对我来说是新的。
    • 好吧,看来我太快把它当作我的问题的解决方案了。
    • 我还在兜圈子。必须完全专门化外部类 MyClass 是不好的。我想利用由 MyClass 的模板参数 专门驱动的内部类 Nested。看来,这不可能?或者它只是“糟糕的设计”——我们必须为此使用外部策略类并且不能将其嵌入到 MyClass 中?
    • @FrankBergemann 是的,。不幸的是,这是不可能的。不过,我不知道您可以使用任何其他设计选择。
    【解决方案2】:

    您无法编译代码似乎有两个原因:

    • 内部模板类的完全特化不能在类中声明。最好的解决方案是添加一个哑默认模板参数,然后声明内部模板类的部分特化。

    • 不能直接使用std::enable_if 禁用模板特化。但是我们可以使用void_t 技巧来做到这一点。 void_t 是 c++17 的一个特性,所以对于早期的 c++ 标准,我们需要提供它的定义。我从http://en.cppreference.com/w/cpp/types/void_t 中获取了void_t 的定义。

    所以你可以重写你的代码如下:

     #include <type_traits>
     #include <iostream>
     #include <type_traits>
    
     //Crédit: cpp-reference.com
     template<typename... Ts> struct make_void { typedef void type;};
     template<typename... Ts> using void_t = typename make_void<Ts...>::type;
    
     template <typename TYPE>
     struct MyStruct
     {
         //The third parameter is an unused parameter 
         template <typename TYPE_AGAIN = TYPE, typename SELECTOR = void
                   , typename = void>
         struct Nested
         {
             static void Print(void)
             {
                 std::cout << "MyStruct::Nested<bool = false>::Print()" << std::endl;
             }
         };
    
         //We declare a partial specialization, T go to the third parameter. We use the void_t trick here.
         template <class T>
         struct Nested<TYPE,
                      void_t<typename std::enable_if<
                                          std::is_integral<TYPE>::value>::type>
                            ,T>
         {
             static void Print(void)
             {
                std::cout << "MyStruct::Nested<bool = true>::Print()" << std::endl;
             }
         }; 
    
     };
    
     int main(){
        MyStruct<int>::Nested<>::Print();
        return EXIT_SUCCESS;
     }
    

    【讨论】:

      【解决方案3】:

      这是一种使它起作用的方法:

      #include <type_traits>
      #include <iostream>
      
      template <typename TYPE>
      struct MyStruct
      {
        template <typename SEL_TYPE = TYPE>
        static typename std::enable_if<std::is_integral<SEL_TYPE>::value && std::is_same<TYPE, SEL_TYPE>::value, void>::type
        Print(void)
        {
          std::cout << "MyStruct::Nested<is_integral::Print()" << std::endl;
        }
      
        template <typename SEL_TYPE = TYPE>
        static typename std::enable_if<!std::is_integral<SEL_TYPE>::value && std::is_same<TYPE, SEL_TYPE>::value, void>::type
        Print(void)
        {
          std::cout << "MyStruct::Nested<not is_integral>::Print()" << std::endl;
        }
      
      };
      

      嵌套结构实际上已经过时了——这只是之前单独建模的遗留物。

      这是我想要合并的 Print() 函数。

      然而,这里需要 std::is_same 来强制 。 否则有人可能会调用

      MyStruct<std::string>::Print<int>();
      

      【讨论】:

      • 有没有办法把这个工作选项变成一个语法糖更少的选项——利用 SEL_TYPE = TYPE?
      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2018-11-26
      • 2023-04-01
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多