C ++强制模板参数

时间:2009-12-26 12:17:00

标签: c++ templates derived avl-tree

我希望这段代码成为可能。

template<typename K, typename T, typename Comparer>
class AVLTree
{
   ...
   void foo() {
       ...
       int res = Comparer::compare(key1, key2);
       ...
   }
   ...
};

具体来说,我想强制Comparer类具有static int compare(K key1, K key2)函数。我正在考虑使用派生,但找不到任何可以使用模板的想法。

谢谢。

8 个答案:

答案 0 :(得分:4)

你有没有尝试过:

   int res = Comparer::compare(key1, key2);

在C ++中,可以通过两种方式调用静态函数:

object.static_method(); // use dot operator

classname::static_method(); // use scope resolution operator

答案 1 :(得分:4)

你做不到。但是如果使用该函数并且Comparer没有它,那么编译将失败,这或多或少是您想要发生的。是的,就像其他人指出的那样,你想把静态称为静态。

答案 2 :(得分:2)

迈克尔already mentioned,如果Comparer没有所需的成员函数,则无法编译。

如果您更担心简明的错误消息,请使用类似this的内容来检测该类是否具有必需的成员函数,并将其与Boost.StaticAssert之类的内容结合使用:

template<typename K, typename T, typename Comparer>
class AVLTree
{
    BOOST_STATIC_ASSERT(HasCompareMethod<Comparer>::has);
    //...
};

答案 3 :(得分:1)

它必须是Comparer::Compare,对吧? (因为你在类型上调用静态函数)。

您的Comparer类必须定义如下:

template<typename K>
class Comparer
{
public:
    static bool Compare(K key, K key)
    {
        return true;
    }
};

答案 4 :(得分:1)

我理解,如果模板参数不符合模板主体的要求,您正在寻找更好的错误消息。该语言没有内置的特殊机制,但人们使用库来近似它。见Boost Concept Check

答案 5 :(得分:1)

对此进行编译时断言的松散方法是获取Comparer :: compare的地址并将其分配给声明为int(K,K)* func的变量。

如果它是一个静态函数,该赋值将起作用,但如果它是一个成员函数则不起作用。

我只是贡献了这一点,但我会采用使用提升习语的方法,因为这是手工卷制的,可以这么说。

答案 6 :(得分:1)

这是一种更惯用和通用的方法:

template<typename K, typename T>
class AVLTree
{
   ...
   template <typename Comparer>
   void foo(Comparer cmp) {
       ...
       int res = cmp(key1, key2);
       ...
   }
   ...
};

Comparer不应该是定义静态Compare方法的类型。它应该是一个可以用函数调用语法调用的类型。这允许您使用函数对象的函数指针,它允许您重用已在标准库中定义的比较器,或几乎任何其他非平凡的C ++应用程序。它允许您在使用lambdas时将它们添加到C ++ 0x中。

强迫Comparer按预期行事?第int res = cmp(key1, key2);行已经确保了这一点。如果您尝试传递无法以这种方式调用的类型,则会出现编译错误。

原始代码中的情况也是如此。如果传递的类型没有静态Compare方法,则会出现编译错误。所以你的原始代码已经解决了这个问题。

答案 7 :(得分:0)

如前所述,您无法强制比较拥有静态成员compare。如果比较器没有实现它,则只会出现编译器错误。

如果您像这样实施AVLTree,将Comparer声明为模板模板参数会更优雅:

template <typename K>
class DefaultComparer
{
public:
    static bool compare(K k1, K k2)
    { return k1 == k2; }
};

template <typename K>
class MyComparer : public DefaultComparer<K>
{
public:
    static bool compare(K k1, K k2)
    { return k1 <= k2; }
};

template <typename K>
class InvalidComparer
{
public:
    static bool bar(K k1, K k2)
    { return k1 != k2; }
    // Doesn't implement compare()
};

// Compare is a template template paramenter with default template argument
// DefaultComparer
template <typename K, template <typename K> class Comparer = DefaultComparer>
class AVLTree
{
    K k1, k2;
public:
    AVLTree() : k1(0), k2(0) { } // ctor
    bool foo();
};

// Definiton of AVLTree::foo()
template <typename K, template <typename K> class Comparer>
bool AVLTree<K, Comparer>::foo()
{
    return Comparer<K>::compare(k1, k2);
}

int main(int argc, char *argv[])
{
    // Without template template parameters you 
    // would have to use AVLTree<int, DefaultComparer<int> >
    AVLTree<int> avltree;

    // instead of AVLTree<int, MyCompare<int> >
    AVLTree<int, MyComparer> avltree2;

    // Calling foo() will generate a compile error. 
    // But if you never call avltree3.foo() this will compile!
    AVLTree<int, InvalidComparer> avltree3;

    avltree.foo(); // calls DefaultComparer::compare
    avltree2.foo(); // calls MyComparer::compare
    avltree3.foo(); // fails to compile
}

请参阅:http://codepad.org/OLhIPjed