0

I am trying to create a compile time polymorphism design that will not require virtual functions with all their drawbacks. However I am struggling with creating simple, effective and easy to understand container that can simulate the ability to hold derived class in it's base class container. My previous attempts with compile time variadic vectors were working, but the code was huge mess. This solutions seems cleaner to me. I have simple code that implements basic CTRP. However I created a runtime container that is storing std::any objects and then based on the type of the object, I can define the action that is supposed to be taken. I have few questions.

  1. How does the usage of std::any and subsequent any_cast<>() hinder the performance compared to the usage of virtual functions?

  2. Is the usage of std::any valid in this situation?

  3. Is there a better way to implement such container?

  4. Is there a way to force implementation as it is with virtual functions (by using virtual <type> foo() = 0)?

  5. Is it a good idea to create an object that will be a CRTP handler? So I will not have a function for CRTP call, but an object, that can manage those calls?

Thank you.

Here is the base class:

class base {
private:
  base() = default;
  friend T;

  T& implementation = static_cast<T&>(*this);
public:
  auto do_stuff() {
    return implementation.do_stuff();
  }
};

Here is the implementation:

#include <iostream>

class implementation_a : public base<implementation_a> {
public:
  auto do_stuff() {
    std::cout << 42 << std::endl;
  }
};

class implementation_b : public base<implementation_b> {
public:
  auto do_stuff() {
    return 420;
  }
};

Here's the container:

#include <vector>
#include <any>

class crtp_vector {
private:
  std::vector<std::any> vec;

public:
  auto begin() {
    return vec.begin();
  }

  auto end() {
    return vec.end();
  }

  auto empty() {
    return vec.empty();
  }

  auto size() {
    return vec.size();
  }

  void clear() {
    vec.clear();
  }

  void push_back(const std::any& val) {
    vec.push_back(val);
  }

  auto emplace_back(const std::any& val) {
    vec.emplace_back(val);
  }
};

Here's the main:

#include "crtp_container.h"

#include <utility>

/* crtp call handler */
template <typename T>
auto crtp_call(T& val) {
  return val.do_stuff();
}

int main() {
  crtp_vector vec;
  implementation_a A;
  implementation_b B;

  vec.push_back(A);
  vec.push_back(B);

  for(auto &member : vec) {
    if(member.type().name() == typeid(implementation_a).name()) {
      crtp_call(std::any_cast<implementation_a&>(member));
    }
    else if(member.type().name() == typeid(implementation_b).name()) {
      std::cout << crtp_call(std::any_cast<implementation_b&>(member)) << std::endl;
    }
    else {
      std::cerr << "no viable type conversion" << std::endl;
    }
  } 

  return 0;
}
4

1 回答 1

2

你把事情弄得太复杂了。显示的代码不base以任何方式使用;如果您只是将其完全删除,什么都不会改变。即使你一直说“CRTP”,你实际上并没有依赖 CRTP 做任何事情。

该代码不使用std::any保存任何类型的能力;它仅用于保存在编译时已知的一组固定类型之一。std::variant对此更好。

总而言之,这个例子归结为:

class implementation_a {
public:
  auto do_stuff() {
    std::cout << 42 << std::endl;
  }
};

class implementation_b {
public:
  auto do_stuff() {
    std::cout << 420 << std::endl;
    return 420;
  }
};

int main() {
  implementation_a A;
  implementation_b B;

  std::vector<std::variant<implementation_a, implementation_b>> vec;
  vec.push_back(A);
  vec.push_back(B);

  for(auto &member : vec) {
      std::visit([](auto& elem) { elem.do_stuff(); }, member);
  } 

  return 0;
}

演示

于 2021-06-20T15:33:46.610 回答