Ошибка функции класса C ++ 14: «У объекта нет атрибута« значение »» - PullRequest
0 голосов
/ 26 июня 2019

РЕДАКТИРОВАТЬ: Всего капитальный ремонт

Я делаю язык программирования (аналог Python), и по крайней мере сейчас, я пытаюсь скомпилировать его в C++.У меня ошибка, когда C++ говорит, что в классе "Object" нет члена "value".Это ошибка компиляции.Я понимаю , почему C++ делает это, так как он должен знать тип value во время компиляции, и поэтому я прошу альтернатив .

Единственное, что ему будет дано, это экземпляры подклассов Object, в которых будет определен член "value", это ошибка компиляции.

Я не могу объявить член "value" внутриObject class, потому что его тип зависит от того, в каком подклассе он находится. Это также должно работать с плавающей точкой и строками, поэтому определение функции с экземпляром Integer вместо экземпляра Object не будет работать.

Здесьэто некоторый код с примером того, как будут использоваться эти объекты:

auto user_i = std::make_unique<Integer>(2);
  std::cout << (*user_i).equals(*std::make_unique<Float>(2.0).get()) << std::endl;

Вот важные определения классов:

  class Object {
  public:
    bool equals(Object& other) {
      throw "Not implemented!";
    }
  };
  class Integer: public Object {
  public:
    int value;
    Integer(int val) {
      value = val;
    }
    bool equals(Object& other) {
      return value == other.value;
    }
  };
class Float: public Object {
  public:
    double value;
    Float(double val) {
      value = val;
    }
    bool equals(Object& other) {
      return value == other.value;
    }
  };
class String: public Object {
  public:
    string value;
    String(string val) {
      value = val;
    }
    bool equals(Object& other) {
      return value.compare(other.value) == 0;
    }
  };

Не должно быть так, что 2 == "2",но это должно быть так, что 2 == 2.0 == 2.000 и т. д.

Затем возникает дополнительное усложнение таких методов, как add (под этим я подразумеваю возвращение значения, все еще не модифицируя объект).Метод equals, предложенный @Michael Karcher, будет работать нормально, но, например, 2+3 должен возвращать 5, а не 5.00000.

. Для еще больших проблем пользовательские Object s (Class s на моем языке) должен иметь возможность переопределять такие методы, как add.

Ответы [ 3 ]

4 голосов
/ 26 июня 2019

В вашем примере есть несколько проблем, помимо основных.Я начну с мелких проблем, чтобы устранить их.

  1. Вам нужно объявить equals как virtual в базовом классе. В отличие от Java , динамическая диспетчеризация не по умолчанию в C ++, но должна запрашиваться для метода с использованием атрибута virtual, например:
class Object {
public:
  virtual bool equals(Object other) {
    throw "Not implemented";
  }
}
Вы передаете объект, который вы сравниваете с по значению . В отличие от Java , в C ++ даже типы классов могут передаваться по значению.Это означает, что функция equals получает копию Object для сравнения с - и просто с этим.Части объекта, с которым вы хотите сравнить, включая элемент value, не копируются в аргумент, передаваемый equals.Вы должны передать параметр по ссылке .Поскольку вашей функции equals не требуется изменять объект, с которым вы сравниваете, достаточно ссылки без разрешения записи (обычно называемой const reference из-за синтаксиса):
class Object {
public:
  virtual bool equals(const Object& other) {
    throw "Not implemented";
  }
}
Если вы пишете базовый класс, который просто предоставляет сигнатуру функции, которую необходимо переопределить в каждом производном классе, вы не заставляете его что-то выдавать, а вместо этого делаете его абстрактным, объявляя его чистым виртуальная функция с использованием синтаксиса =0.Это предотвращает случайное создание Object экземпляров, которые нельзя сравнивать.Это могло бы отразить пропущенную передачу по ссылке:
class Object {
public:
  virtual bool equals(const Object& other) = 0;
}

Теперь давайте ответим на ваш вопрос:

Этот подход работает в динамически типизированных языках, таких как JavaScript или Python, ноэто не работает в C ++. Во время компиляции компилятор должен знать, где он находит элемент value в объекте other и его тип.Если вы просто передадите любой Object, компилятор не сможет это узнать.И даже вы не знаете: тип может быть int или float.Так что ни вы, ни компилятор не знают, есть ли в объекте, который вы передаете для сравнения, значение с плавающей запятой или значение int.Если объекты Integer должны быть сопоставимы как с объектами Integer, так и Float, вам необходимо либо использовать методы сравнения, либо вам нужен способ получить общий value общего типа.В этом случае на машинах с 32-разрядными целыми числами каждое целочисленное значение точно представляется в переменной double .Вы можете добавить в класс Object вторую функцию с именем as_double следующим образом:

class Object {
public:
  virtual bool equals(const Object &other) const = 0;
  virtual double as_double() const = 0;
}

Я также пометил методы const, что означает, что вы можете вызывать их на объектах или использовать ссылки, которые вы можетене пишите.Теперь вы можете реализовать Integer и Float следующим образом:

class Integer: public Object {
public:
  int value;
  Integer(int val) {
    value = val;
  }
  bool equals(const Object & other) const {
    return value == other.as_double();
  }
  double as_double() const {
    return value;
  }
};
class Float: public Object {
public:
  double value;
  Float(double val) {
    value = val;
  }
  bool equals(const Object & other) const {
    return value == other.as_double();
  }
  double as_double() const {
    return value;
  }
};

И, если вы посмотрите на метод equals, он теперь почти одинаков для обоих типов: вы извлекаете значение аргументакак double, и сравните его с локальным значением (в случае Integer локальное значение тоже неявно преобразуется в double. Таким образом, вы также можете использовать универсальную реализацию сравнения, которая вызывает to_double для обоих объектов, и выне нужно беспокоиться о реализации equals в каждом подклассе:

class Object {
public:
  bool equals(const Object& other) const {
    return as_double() == other.as_double();
  }
  virtual double as_double() const = 0;
}

Обратите внимание, это работает только потому, что double на самом деле может представлять все значения, даже те, которые хранятся в Integers.Фактический вариант использования не имеет такого общего типа, который вы можете преобразовать, вам нужно более сложное решение.

1 голос
/ 26 июня 2019

Обратите внимание, что:

  • C ++ - это не язык с динамической типизацией, а язык со статической типизацией.Тип переменной определяется во время компиляции, а не во время выполнения.
  • Чтобы иметь динамический характер с типом, вам необходим класс polymorphic - т.е. класс, имеющий виртуальные функции.С помощью одной или нескольких виртуальных функций вы получаете динамическую природу типа (класса).Производный тип (класс) будет (повторно) реализовывать необходимые виртуальные функции

Если вы хотите иметь типы, которые кажутся динамическими, но статичными во время компиляции;тогда вы можете использовать шаблоны.Например, функция Add может быть:

template<typename Type>
Type Add(Type a, Type b)
{
    return a+b;
}

И вызывать их с любым типом:

Add(1,2); // int
Add(4.5, 5.0); // double

В функции, если вы хотите узнать тип, вы можетеиспользуйте определенные вспомогательные функции, такие как is_same:

Type Add(Type a, Type b)
{
   if(std::is_same<Type, int>::value) 
   {
       // Do whatever when 'int' is passed
   }
  ...
}

Такой же подход можно использовать и в шаблонах классов .Вы можете иметь (частичную) специализацию класса шаблона.vector с типом bool является одним примером.

std::distance и std::advance - другие примеры, которые основаны на типе контейнера (в простом смысле) и реализованы по-разному (произвольный доступ или последовательный доступ).

0 голосов
/ 26 июня 2019

Функции равенства используют Object в качестве типа параметров.Вызов other.value не работает, как вы уже указали, у объекта нет значения параметра.С точки зрения компилятора он никогда не получит ничего со значением параметра.

Я предлагаю переписать определения функций от equals до bool equals(Integer& other) и bool equals(Float& other).

...