【问题标题】:C++: Default values for template arguments other than the last ones?C++:模板参数的默认值,而不是最后一个?
【发布时间】:2010-11-15 23:53:02
【问题描述】:

我的模板化容器类如下所示:

template<
   class KeyType, 
   class ValueType, 
   class KeyCompareFunctor   = AnObnoxiouslyLongSequenceOfCharacters<KeyType>, 
   class ValueCompareFunctor = AnObnoxiouslyLongSequenceOfCharacters<ValueType> 
>
   class MyClass
   {
      [...]
   }

这意味着当我实例化这个类的一个对象时,我可以通过几种不同的方式来实现:

MyClass<MyKeyType, MyValueType> myObject;
MyClass<MyKeyType, MyValueType, MyCustomKeyCompareFunctor> myObject;
MyClass<MyKeyType, MyValueType, MyCustomKeyCompareFunctor, MyCustomValueCompareFunctor> myObject;

这些都很好。当我想实例化一个使用非默认版本的 ValueCompareFunctor 参数的 MyClass 时,问题就出现了,但我仍然想使用 KeyCompareFunctor 参数的默认值。然后我必须写这个:

MyClass<MyKeyType, MyValueType, AnObnoxiouslyLongSequenceOfCharacters<MyKeyType>, MyCustomValueCompareFunctor> myObject;

如果我能以某种方式省略第三个参数而只写这个会更方便:

MyClass<KeyType, ValueType, MyCustomValueCompareFunctor> myObject;

由于 MyCustomValueCompareFunctor 仅适用于 MyValueType 类型的对象,而不适用于 MyKeyType 类型的对象,因此编译器似乎至少在理论上可以理解我的意思。

有没有办法在 C++ 中做到这一点?

【问题讨论】:

  • 您也许可以通过元编程技巧来做一些事情。但是想想当你的键和值类型相同时会发生什么。为什么不使用 typedef 进行实例化? (注意 C++0X 有模板别名,将来可以帮助你。)
  • 您可能已经尝试过:如果使用的 KeyCompareFunctor 更频繁地是默认值,则在定义中切换 KeyCompareFunctor 和 ValueCompareFunctor。这样你就可以省略最后一个模板参数。

标签: c++ templates arguments default


【解决方案1】:

一般来说,无论是在模板还是在函数或方法中,C++ 都允许您对(从而省略)尾随 参数使用默认值——没有出路。

我建议使用模板或宏将 AnObnoxiouslyLongSequenceOfCharacters&lt;MyKeyType&gt; 缩短为 Foo&lt;MyKeyType&gt; -- 并不完美,但总比没有好。

【讨论】:

    【解决方案2】:

    没有。最接近的方法是允许用户指定一些哨兵类型 - 比如void - 意思是“在此处使用默认值”,并在你的类中使用模板超魔法到typedef 如果void 是给你的真正默认值.但从可读性的角度来看,这可能不是一个好主意。

    【讨论】:

      【解决方案3】:

      Boost parametersBoost graph named parameters 致力于为模板函数/方法命名参数。他们提供了以您喜欢的任何顺序提供论据的机会。一些参数可能是可选的,具有默认值。

      同样的方法可以应用于模板参数。不要使用 N 模板参数 + P 可选参数,而是使用 N+1 模板参数创建您的类。最后一个将包含可以省略的“命名”参数。

      这个答案还不完整,但我希望这是一个好的开始!

      【讨论】:

        【解决方案4】:

        另一种选择是使用 Traits 类:

        template <class KeyType>
        class KeyTraits
        {
          typedef AnObnoxiouslyLongSequenceOfCharacters<KeyType> Compare;
        };
        
        template <class ValueType>
        class ValueTraits
        {
          typedef AnObnoxiouslyLongSequenceOfCharacters<ValueType>  Compare;
        };
        
        template<class KeyType class ValueType>
        class MyClass
        {
          typedef KeyTraits<KeyType>::Compare KeyCompareFunctor;
          typedef ValueTraits<ValueType>::Compare KeyCompareFunctor;
        };
        

        然后,如果您的类型需要不同的 Key 比较函数,那么您将明确专门针对这种情况使用 KeyTraits 类型。这是我们将其更改为 int 的示例:

        template <>
        class KeyTraits<int>
        {
          typedef SpecialCompareForInt Cmopare;
        };
        

        【讨论】:

          【解决方案5】:

          还有另一个选项,它使用继承,其工作方式如下。对于最后两个参数,它使用一个类,该类实际上继承自一个具有两个成员模板的类,可用于生成所需的类型。因为继承是虚拟的,所以它声明的 typedef 在继承之间共享,如下所示。

          template<class KeyType, 
                   class ValueType, 
                   class Pol1 = DefaultArgument, 
                   class Pol2 = DefaultArgument>
          class MyClass {
              typedef use_policies<Pol1, Pol2> policies;
          
              typedef KeyType key_type;
              typedef ValueType value_type;
              typedef typename policies::
                template apply_key_compare<KeyType>::type 
                key_compare;
              typedef typename policies::
                template apply_value_compare<ValueType>::type 
                value_compare;
          };
          

          现在,有一个你使用的默认参数,它有你想要提供的默认参数的类型定义。成员模板将通过键和值类型参数化

          struct VirtualRoot { 
            template<typename KeyType>
            struct apply_key_compare {
              typedef AnObnoxiouslyLongSequenceOfCharacters<KeyType> 
                type;
            };
            template<typename ValueType>
            struct apply_value_compare {
              typedef AnObnoxiouslyLongSequenceOfCharacters<ValueType> 
                type;
            };
          };
          
          struct DefaultArgument : virtual VirtualRoot { };
          
          template<typename T> struct KeyCompareIs : virtual VirtualRoot {
            template<typename KeyType>
            struct apply_key_compare {
              typedef T type;
            };
          };
          
          template<typename T> struct ValueCompareIs : virtual VirtualRoot {
            template<typename ValueType>
            struct apply_value_compare {
              typedef T type;
            };
          };
          

          现在,use_policies 将派生自所有模板参数。如果VirtualRoot 的派生类对基类隐藏了一个成员,则派生类的该成员对基类的成员具有支配性,并且将被使用,即使基类成员可以通过其他路径访问继承树。

          请注意,您无需为虚拟继承付费,因为您从不创建 use_policies 类型的对象。您只使用虚拟继承来利用优势规则。

          template<typename B, int>
          struct Inherit : B { };
          
          template<class Pol1, class Pol2>
          struct use_policies : Inherit<Pol1, 1>, Inherit<Pol2, 2>
          { };
          

          因为我们可能不止一次地从同一个类派生,所以我们使用类模板Inherit:禁止直接继承同一个类两次。但是允许间接继承它。你现在可以像下面这样使用它:

          MyClass<int, float> m;
          MyClass<float, double, ValueCompareIs< less<double> > > m;
          

          【讨论】:

            猜你喜欢
            • 2016-02-14
            • 2012-08-07
            • 2012-05-22
            • 1970-01-01
            • 2015-05-09
            • 1970-01-01
            • 2011-07-15
            • 2018-07-16
            • 2018-07-20
            相关资源
            最近更新 更多