用C++实现匿名类接口

Implement Interface with Anonymous Class in C++

本文关键字:接口 C++ 实现      更新时间:2023-10-16

在java中,我们可以用匿名类实现interface,如下所示:

import java.util.function.Predicate;
public class Test {
    public static void main(String[] args) {
        System.out.println(testIf("", new Predicate<String>() {
            @Override
            public boolean test(String s) {
                return s.isEmpty();
            }
        }));
    }
    public static <T> boolean testIf(T t, Predicate<T> predicate) {
        return predicate.test(t);
    }
}

自Java 8:

System.out.println(testIf("", String::isEmpty));

我们如何在c++中做到这一点?我写了以下代码,但我得到了编译错误:

#include "stdafx.h"
#include <iostream>
#include <string>
using namespace std;
template <class T>
class Predicate
{
public:
    virtual bool test(T) = 0;
};
template <class T>
bool testIf(T t, Predicate<T> predicate)
{
    return predicate.test(t);
}
int main()
{
    class : public Predicate <string> {
    public:
        virtual bool test(string s)
        {
            return s.length() == 0;
        }
    } p;
    string s = "";
    cout << testIf(s, p);
    cin.get();
    return 0;
}

错误:没有函数模板"testIf"的实例与参数列表匹配参数类型为:(std::stringclass <unnamed>

这里怎么了?有其他方法吗?

谢谢!

这里怎么了?

为了使多态性发挥作用,您需要通过引用(或者指针,如果您愿意的话)传递基类:

bool testIf(T t, Predicate<T> & predicate)
                              ^

修复后,代码对我来说是有效的,尽管它闻起来太像Java了。

有其他方法吗?

在C++11或更高版本中,lambda在性能和代码噪声方面都会更高效。它消除了定义基类和从基类继承以及调用虚拟函数的需要。

template <class T, class Predicate>
bool testIf(T t, Predicate predicate)
{
    return predicate(t);
}
testIf(s, [](string const & s){return s.length() == 0;});

(在C++11之前,你可以对函数对象做同样的事情。它稍微更详细,但仍然比继承方法提供更短、更高效的代码。)

C++只支持引用和指针类型上的多态性。将您的方法更改为

template <class T>
bool testIf(T t, Predicate<T> & predicate) 
{
    return predicate.test(t);
}

那么您也可以将Predicate的子类传递给这个函数。

您可以为函数使用一个指针:

#include "stdafx.h"
#include <iostream>
#include <string>
using namespace std;
template <class T>
class Predicate
{
public:
    bool (*test)(T);
};
template <class T>
bool testIf(T t, Predicate<T> predicate)
{
    return predicate.test(t);
}
bool one_test(string e)
{
    return e.length() == 0;
}
int main()
{
    Predicate<string> p;
    p.test = one_test;
    string s = "";
    cout << testIf(s, p);
    cin.get();
    return 0;
}

为了使这种(多态性)发挥作用,您必须通过指针或引用将谓词传递给testIF:

template <class T>
bool testIf(T t, Predicate<T>* predicate)
{
    if (predicate == nullptr) return true;
    return predicate->test(t);
}
int main()
{
    class : public Predicate <string> {
    public:
        virtual bool test(string s)
        {
            return s.length() == 0;
        }
    } p;
    string s = "";
    cout << testIf(s, &p);
    cin.get();
    return 0;
}

我通常更喜欢使用指针,因为当使用引用时,我倾向于忽略多态性的可能性。另一方面,它有disatvantage,你必须检查nullptr,语法也不像以前那么好了。

实现这一点的一种更常见的方法是使用lambdas,例如:

template <class T,class Pred>
bool testIf2(T t, Pred predicate)
{
    return predicate(t);
}
int main()
{
    auto pred = [](const string& s){return s.length() == 0; };
    string s = "";
    cout << testIf2(s, pred );
    cin.get();
    return 0;
}