1

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

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)功能。我正在考虑使用派生,但找不到任何可以使用模板的想法。

谢谢你。

4

8 回答 8

5

你不能。但是如果使用该功能而比较器没有它,您的编译将失败,这或多或少是您想要发生的。是的,就像其他人指出的那样,您想将静态称为静态。

于 2009-12-26T12:28:52.693 回答
4

您是否尝试过:

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

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

object.static_method(); // use dot operator

classname::static_method(); // use scope resolution operator
于 2009-12-26T12:20:51.497 回答
2

Michael已经提到,如果Comparer没有所需的成员函数,它将无法编译。

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

template<typename K, typename T, typename Comparer>
class AVLTree
{
    BOOST_STATIC_ASSERT(HasCompareMethod<Comparer>::has);
    //...
};
于 2009-12-26T12:51:05.317 回答
1

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

您的 Comparer 类必须定义如下:

template<typename K>
class Comparer
{
public:
    static bool Compare(K key, K key)
    {
        return true;
    }
};
于 2009-12-26T12:21:40.960 回答
1

我了解您正在寻找更好的错误消息,以防模板参数不符合模板正文的要求。该语言没有内置的特殊机制,但人们使用库来近似它。见Boost 概念检查

于 2009-12-26T12:52:44.830 回答
1

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

如果它是静态函数,则该分配将起作用,但如果它是成员函数,则该分配将不起作用。

我只是在贡献这个,但我会采取使用 boost 成语的方法,因为这是手动滚动的,可以这么说。

于 2009-12-26T12:59:18.063 回答
1

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

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

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

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

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

于 2009-12-26T21:40:16.003 回答
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

于 2009-12-26T14:49:12.440 回答