在C++中使用匿名类实现接口

6
在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>

这里出了什么问题?还有其他的解决方法吗?

谢谢!


2
你可以在https://dev59.com/WEfSa4cB1Zd3GeqPBv7-上查看。 - Prashant
4个回答

9

这里有什么问题?

为了实现多态性,你需要通过引用(或指针,如果你喜欢)传递基类:

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 之前,您可以使用函数对象来完成相同的事情。虽然略微冗长,但仍比继承方法提供了更短、更高效的代码。)

3

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

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

此外,您还可以将Predicate的子类传递给此函数。


那也是真的,我没看到 :) - amchacon

3
您可以使用一个指针来调用函数:
#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;
}

3
为了使多态性生效,您需要通过指针或引用将谓词传递到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;
}

我通常更喜欢使用指针,因为当使用引用时,我往往会忽视多态的可能性。另一方面,它的缺点是你必须检查nullptr,并且语法不如以前那么好看。

更常见的实现方式是使用lambda表达式,例如:

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;
}

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接