【问题标题】:C++: undefined reference to the functor's overloaded invocation operatorC ++:对仿函数的重载调用运算符的未定义引用
【发布时间】:2020-09-15 08:26:27
【问题描述】:
template <typename T>
class Predicate {
   public:
    bool operator()(const T& x) const;
};

template <typename T>
class LessThan : public Predicate<T> {
   public:
    explicit LessThan(const T& v) : val(v) {}
    bool operator()(const T& x) const { return x < val; }

   private:
    const T val;
};

template <typename C, typename T>
class Producer {
   public:
    T operator()(const C& c) const;
};

template <typename C, typename V>
class HowMuch : public Producer<C, int> {
   public:
    explicit HowMuch(Predicate<V> p) : predicate{p} {}
    int operator()(const C& c) const {
        int count = 0;
        for (const auto& x : c)
            if (predicate(x)) ++count;
        return count;
    }

   private:
    Predicate<V> predicate;
};

int main() {
    const LessThan<int> lf(5);
    const HowMuch<list<int>, int> hm(lf);
    list<int> li {1, 2, 3, 4, 5, 6, 7, 8, 9, 10};
    cout << "How much numbers less than 5 is in {1, 2, 3, 4, 5, 6, 7, 8, 9, "
            "10}? Answer: "
         << hm(li)
         << endl;
}

在编译上述代码时,g++ 会将其打印到控制台:

/tmp/ccblK6El.o:在函数HowMuch&lt;std::__cxx11::list&lt;int, std::allocator&lt;int&gt; &gt;, int&gt;::operator()(std::__cxx11::list&lt;int, std::allocator&lt;int&gt; &gt; const&amp;) const: 模板.cpp:(.text._ZNK7HowMuchINSt7__cxx114listIiSaIiEEEiEclERKS3_[_ZNK7HowMuchINSt7__cxx114listIiSaIiEEEiEclERKS3_]+0x84): 未定义对Predicate&lt;int&gt;::operator()(int const&amp;) const 的引用 collect2: error: ld returned 1 exit status 终端进程 以退出代码终止:1

我不太明白HowMuch 中的Prediate&lt;V&gt; 定义有什么问题,因为对我(C++ 新手)来说,它看起来真的是 LGTM。根据我的理解,编译器将Predicate&lt;int&gt; 的定义创建为单独的类型,并且日志准确地说明了这一点,但由于某种原因,它找不到重载调用运算符的类型化定义。可能是类型扣除的问题?容器模板类型本身的模板必须以某种方式显式定义?

编辑:

virtual 修饰符被添加到PredicateProducer 的函数运算符重载中,但问题似乎仍然存在。但是,错误“描述”(如果可以称为有用的描述)有所更改(但仍然指向相同的问题):

/tmp/ccn1Swqa.o: 在函数 HowMuch >, int>::operator()(std::__cxx11::list > const&) const: 模板.cpp:(.text._ZNK7HowMuchINSt7__cxx114listIiSaIiEEEiEclERKS3_[_ZNK7HowMuchINSt7__cxx114listIiSaIiEEEiEclERKS3_]+0x76): 未定义对 Predicate::operator()(int const&) const 的引用 /tmp/ccn1Swqa.o:(.rodata._ZTV8ProducerINSt7__cxx114listIiSaIiEEEiE[_ZTV8ProducerINSt7__cxx114listIiSaIiEEEiE]+0x10): 对生产者的未定义引用 >, int>::operator()(std::__cxx11::list > const&) const /tmp/ccn1Swqa.o:(.rodata._ZTV9PredicateIiE[_ZTV9PredicateIiE]+0x10): 未定义对 Predicate::operator()(int const&) const 的引用 collect2: error: ld returned 1 exit status 终端进程 以退出代码终止:1

【问题讨论】:

  • 您似乎想要 C++20 概念,而不是这种不必要的继承。
  • @Jarod42 我正在学习 C++ 作为一种支持语言,而不是更多,所以从 Stroustrup 的 C++11 书直接跳到 c++20 标准似乎是不够的,尤其是当问题已经解决时
  • Predicate的目的是什么?似乎不需要。确实当前的答案修复了编译问题,但基类在这里似乎没用。
  • @Jarod42 这段代码显然除了练习我从书中得到的特性之外没有任何用途,而在这里我只是想创建一个 Java 8 端口的功能接口层次结构,看看它是否有效正如预期的那样。它与原始问题有什么关系?但是,无论目的是什么,我都不会阻止您使这段代码成为理想的 sn-p :)
  • Java 解决问题的方式不需要 C++ 方式(对于这种情况)

标签: c++ c++11 functor


【解决方案1】:

在您的情况下,继承似乎不合理,您可能会摆脱基类:

template <typename T>
class LessThan {
public:
    explicit LessThan(const T& v) : val(v) {}
    bool operator()(const T& x) const { return x < val; }

private:
    T val;
};

template <typename Predicate>
class HowMuch {
   public:
    explicit HowMuch(Predicate p) : predicate{p} {}

    template <typename Container>
    int operator()(const Container& c) const {
        int count = 0;
        for (const auto& x : c)
            if (predicate(x)) ++count;
        return count;
    }

private:
    Predicate predicate;
};

int main() {
    const LessThan<int> lf(5);
    const HowMuch<LessThan<int>> hm(lf);
    std::list<int> li {1, 2, 3, 4, 5, 6, 7, 8, 9, 10};

    std::cout << "How much numbers less than 5 is in {1, 2, 3, 4, 5, 6, 7, 8, 9, "
            "10}? Answer: "
         << hm(li)
         << std::endl;
}

Demo

如果你真的想要基类作为接口,你需要纯虚调用

template <typename T>
class Predicate {
public:
    virtual ~Predicate() = default;
    virtual bool operator()(const T& x) const = 0;
};

template <typename C, typename T>
class Producer {
public:
    virtual ~Producer() = default;
    virtual T operator()(const C& c) const = 0;
};

您还需要修复您拥有的对象切片:

template <typename C, typename V>
class HowMuch : public Producer<C, int> {
   public:
    explicit HowMuch(const Predicate<V>& p) : predicate{&p} {}
    int operator()(const C& c) const override {
        int count = 0;
        for (const auto& x : c)
            if ((*predicate)(x)) ++count;
        return count;
    }

private:
    const Predicate<V>* predicate;
};

Demo

【讨论】:

  • 如果我想创建其他几个具有静态声明的Predicate 实现,如何实现强类型化,因此,在编译时评估,如果接口继承被丢弃,重载调用运算符?使谓词只是一个类型参数将编译和运行程序看起来很好,因此如果作为Predicate 实现者放置的对象\ lambda 不会返回应该表示的值,则结果将是未定义的布尔结果。
  • 关于答案的第二部分 - 它适合解决所述问题,因此给予支持
  • 如果你给了错误的谓词,你将有编译时错误。模板提供强类型,即使它允许 duck typing
  • 鸭子类型意味着运行时方法解析,不是吗?如果请求的方法不存在,如何避免运行时错误?
  • 据我所见,可伸缩性(不能轻易添加不同的实现,因为它们现在是临时的)和可替代性(不能有一个类型为 HowMuch&lt;PredicateInterface&gt; 的变量包含值HowMuch&lt;SpecificPredicate&gt;) 这个小“解决方案”,但作为回报,它允许减少代码库的大小。不安全编程提供的基本权衡,还是我错了?
【解决方案2】:

如果你想让 Predicate 成为一个抽象类,你需要:

-将 bool operator() 方法设为虚拟并将其设置为 0(在 Predicate 类中):

virtual bool operator()(const T& x) const=0;

-在 HowMuch 中存储对谓词的引用:

explicit HowMuch(Predicate<V> &p) : predicate{p} {}
Predicate<V> &predicate;

【讨论】:

  • 是的,谢谢,@FMeinicke 回答了这个问题,您的回答在“纯虚拟”的上下文中更加明确。投了赞成票:)
【解决方案3】:

您需要为类的所有功能提供定义。这意味着即使您只从 PredicateProducer 派生类,您仍然必须在这些类中实现 operator()

如果您不想这样做(即只有函数声明但没有定义),请考虑通过声明operator() 方法纯虚拟使这两个类抽象 /强>。然后,您不能直接从这些类实例化对象,而只能从实现operator() 方法的派生类实例化。这也意味着您只能在 HowMuch 构造函数中传递 Predicate&lt;V&gt;*

【讨论】:

  • 指针转换与 virtual=0 的函数结合成功了,谢谢!
猜你喜欢
  • 2018-09-17
  • 2016-01-16
  • 1970-01-01
  • 2012-06-24
  • 1970-01-01
  • 1970-01-01
  • 2011-01-27
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多