我的模板化容器类看起来像这样:
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 ++中执行此操作?
答案 0 :(得分:5)
通常,在模板和函数或方法中,C ++都允许您使用默认值(从而省略)尾随参数 - 没有出路。
我建议使用模板或宏来缩短AnObnoxiouslyLongSequenceOfCharacters<MyKeyType>
到Foo<MyKeyType>
- 不完美,但总比没有好。
答案 1 :(得分:4)
没有。你最接近的是允许用户指定一些哨兵类型 - 比如void
- 意思是“在这里使用默认值”,并在你的班级中使用模板metamagic typedef
真正的默认值{{1给了你。但从可读性的角度来看,这可能不是一个好主意。
答案 2 :(得分:3)
Boost parameters和Boost 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;