最新消息:雨落星辰是一个专注网站SEO优化、网站SEO诊断、搜索引擎研究、网络营销推广、网站策划运营及站长类的自媒体原创博客

c++ - How do I define a pure virtual function to return a view to a container - Stack Overflow

programmeradmin2浏览0评论

Lets say I want to define an abstract class called A which has the virtual method get_range which should return a set of numbers. Sub classes of it will override the method get_range to return different numbers.

Here is my try:

#include <iostream>
#include <ranges>
#include <vector>

class A {
  public:
  virtual auto get_range() const -> std::ranges::view auto {
    return std::views::all(data);
  }

  protected:
  std::vector<int> data = {1, 2, 3, 4};
};

class B : public A {
  public:
  virtual auto get_range() const -> std::ranges::view auto override {
    std::cout << "B::get_range().." << std::endl;
    return data | std::views::transform([](int x) { return x * 2; });
  }
};

class C : public A {
  public:
  virtual auto get_range() const -> std::ranges::view auto override {
    std::cout << "C::get_range().." << std::endl;
    return data | std::views::transform([](int x) { return x * 3; });
  }
};

int main() {
  A* a = new A();
  A* b = new B();
  A* c = new C();

  for (auto x : a->get_range()) {
    std::cout << x << " ";
  }
  std::cout << std::endl;

  for (auto x : b->get_range()) {
    std::cout << x << " ";
  }
  std::cout << std::endl;

  for (auto x : c->get_range()) {
    std::cout << x << " ";
  }
  std::cout << std::endl;

  delete a;
  delete b;
  delete c;

  return 0;
}

I expect numbers returned by B and C to be multiplications of original numbers in the vector multiplied by 2 and 3. But What it print is the original vector.

What am I doing wrong here?

Lets say I want to define an abstract class called A which has the virtual method get_range which should return a set of numbers. Sub classes of it will override the method get_range to return different numbers.

Here is my try:

#include <iostream>
#include <ranges>
#include <vector>

class A {
  public:
  virtual auto get_range() const -> std::ranges::view auto {
    return std::views::all(data);
  }

  protected:
  std::vector<int> data = {1, 2, 3, 4};
};

class B : public A {
  public:
  virtual auto get_range() const -> std::ranges::view auto override {
    std::cout << "B::get_range().." << std::endl;
    return data | std::views::transform([](int x) { return x * 2; });
  }
};

class C : public A {
  public:
  virtual auto get_range() const -> std::ranges::view auto override {
    std::cout << "C::get_range().." << std::endl;
    return data | std::views::transform([](int x) { return x * 3; });
  }
};

int main() {
  A* a = new A();
  A* b = new B();
  A* c = new C();

  for (auto x : a->get_range()) {
    std::cout << x << " ";
  }
  std::cout << std::endl;

  for (auto x : b->get_range()) {
    std::cout << x << " ";
  }
  std::cout << std::endl;

  for (auto x : c->get_range()) {
    std::cout << x << " ";
  }
  std::cout << std::endl;

  delete a;
  delete b;
  delete c;

  return 0;
}

I expect numbers returned by B and C to be multiplications of original numbers in the vector multiplied by 2 and 3. But What it print is the original vector.

What am I doing wrong here?

Share Improve this question edited Feb 15 at 5:56 peter asked Feb 15 at 4:32 peterpeter 1179 bronze badges 11
  • 1 Pure or not pure virtual, not related and affected return type, etc. So unclear about what you ask – RbMm Commented Feb 15 at 5:07
  • It would be helpful to show how you want the code to work. Is it evenseries.get_number() | views::transform(...)? When you try to do it with a virtual function, the return type has to be the same type, with a minor exception that I don't think applies here. Do you want each class to return a different container type? – Rud48 Commented Feb 15 at 5:08
  • I completely rewrote it. I managed it to compile without a pure virtual function (As now, since it is not pure, compiler can deduce the return type through the given implementation). But still, the returned numbers are not transformed. – peter Commented Feb 15 at 5:57
  • You need to define type erasure for views, then return an instance of erased type in your virtual functions. That's very costly in terms of runtime; a very poor design choice. – Red.Wave Commented Feb 15 at 6:40
  • 2 It seems like you need a type-erasable view such as any_view, although the standard currently doesn't have one. – 康桓瑋 Commented Feb 15 at 7:51
 |  Show 6 more comments

1 Answer 1

Reset to default 1

Live code

The code compiles and runs with GCC 14.2, but the output is wrong. CLang 19.1 generates the error function with deduced return type cannot be virtual. I'm going with CLang being correct.

The GCC output shows that C::get_range is called with ca->get_range, and the value inside the function is transformed. But the returned value is the original value from A. Commenting the println in get_range doesn't change the returned value.

When called with cc, the returned value is transformed, as expected.

#include <ranges>
#include <vector>

namespace rng = std::ranges;
namespace vws = std::views;

#include <fmt/ranges.h>
using fmt::println, fmt::print;

class A {
public:
   virtual ~A() = default;
   [[nodiscard]] virtual auto get_range() const -> std::ranges::view auto {
      return std::views::all(data);
   }

protected:
   std::vector<int> data = {1, 2, 3, 4};
};

class C : public A {
public:
   [[nodiscard]] auto get_range() const -> std::ranges::view auto override {
      auto mod_data = data | std::views::transform([](int x) {
         return x * 3;
      });
      println("gr {}", mod_data);
      return mod_data;
   }
};

auto main() -> int {
   A* a = new A();
   A* ca = new C();
   C* cc = new C();

   println("a {}\n", a->get_range());
   println("ca {}\n", ca->get_range());
   println("cc {}\n", cc->get_range());

   delete a;
   delete ca;
   delete cc;

   return 0;
}

GCC output.

 [1, 2, 3, 4]

gr [3, 6, 9, 12]    << C::get_range called !!!
ca [1, 2, 3, 4]     << why?

gr [3, 6, 9, 12]
cc [3, 6, 9, 12]
发布评论

评论列表(0)

  1. 暂无评论