C ++:除最后一个之外的模板参数的默认值?

时间:2009-08-01 04:33:55

标签: c++ templates arguments default

我的模板化容器类看起来像这样:

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 ++中执行此操作?

5 个答案:

答案 0 :(得分:5)

通常,在模板和函数或方法中,C ++都允许您使用默认值(从而省略)尾随参数 - 没有出路。

我建议使用模板或宏来缩短AnObnoxiouslyLongSequenceOfCharacters<MyKeyType>Foo<MyKeyType> - 不完美,但总比没有好。

答案 1 :(得分:4)

没有。你最接近的是允许用户指定一些哨兵类型 - 比如void - 意思是“在这里使用默认值”,并在你的班级中使用模板metamagic typedef真正的默认值{{1给了你。但从可读性的角度来看,这可能不是一个好主意。

答案 2 :(得分:3)

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

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

这个答案还没有完成,但我希望这是一个好的开始!

答案 3 :(得分:0)

另一种选择是使用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;
};

答案 4 :(得分:0)

还有另一个选项,它使用继承,其工作方式如下。对于最后两个参数,它使用一个类,该类实际上从具有两个成员模板的类继承,可用于生成所需的类型。因为继承是虚拟的,所以它声明的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;
};

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

进行参数化
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;