5

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

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++ 中做到这一点?

4

5 回答 5

5

通常,无论是在模板还是函数或方法中,C++ 都允许您对(并因此省略)仅尾随参数使用默认值——没有出路。

我推荐一个模板或宏来缩短AnObnoxiouslyLongSequenceOfCharacters<MyKeyType>--Foo<MyKeyType>不完美,但总比没有好。

于 2009-08-01T04:39:29.527 回答
4

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

于 2009-08-01T04:39:01.243 回答
3

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

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

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

于 2009-08-01T09:38:28.033 回答
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;
};
于 2009-08-03T10:29:45.517 回答
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;
};

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

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基类隐藏成员,则派生类的该成员对基类成员具有支配性,并且将被使用,即使该基类成员可以通过继承树中的其他路径到达。

请注意,您不需要为虚拟继承付费,因为您从不创建 type 的对象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;
于 2009-08-28T23:48:57.997 回答