Управление неявным преобразованием типов в C ++

StackOverflow https://stackoverflow.com/questions/1051089

  •  20-08-2019
  •  | 
  •  

Вопрос

Я работаю над кодом, который выполняет запросы ближайшего соседа.Есть две простые идеи, лежащие в основе того, как пользователь может запрашивать данные в поиске:

  • ближайшие N точек к данной точке в пространстве.
  • все точки в пределах заданного расстояния.

В моем коде точки помещаются в pointList, а pointList - это контейнер, задачей которого является отслеживание точек, найденных при поиске.

Прямо сейчас мой объект pointList имеет один конструктор:

PointList( unsigned int maxvals ); // #1

Следующие два конструктора, которые я хотел бы добавить, следующие:

PointList( float maxdist ); // #2
PointList( unsigned int maxvals, float maxdist ); // #3

Мой вопрос заключается в следующем:как мне гарантировать, что мои пользователи и компилятор C ++ сгенерируют правильный конструктор для списка точек и проводит различие между конструкторами 1 и 2?Должен ли я просто реализовать # 3 и предоставить константы, которые определяют произвольно большие значения для maxvals и maxdist?Другой альтернативой могло бы быть написание другой системы легковесных объектов, которые управляют логикой добавления точек в список, но это кажется излишеством для такой простой идеи.

Я действительно пытаюсь сделать это прозрачным для моих пользователей, которые в основном являются учеными, которые изучали C ++ иногда без получения формального образования.Спасибо!

Это было полезно?

Решение

Разрешение перегрузки для целочисленных типов происходит по двум категориям, которые можно очень грубо свести к

  • Продвижение по службе:Это преобразование из типов, меньших, чем int Для int или unsigned int, в зависимости от того , является ли int может хранить все значения исходного типа.
  • Преобразование:Это преобразование из любого целочисленного типа в другой целочисленный тип.

Аналогично, преобразование для типов с плавающей запятой происходит в двух категориях

  • Продвижение по службе:Это преобразование из float Для double
  • Преобразование:Это преобразование из любого типа с плавающей запятой в другой тип с плавающей запятой

И происходит преобразование из целого числа в плавающее или обратно.Это оценивается как конверсия, а не как продвижение по службе.Продвижение оценивается лучше, чем конверсия, и там, где требуется только продвижение, этот вариант будет предпочтительнее.Таким образом, вы можете использовать следующие конструкторы

PointList( int maxVals );
PointList( unsigned int maxVals );
PointList( long maxVals );
PointList( unsigned long maxVals );

PointList( double maxDist );
PointList( long double maxDist );

Для любого целочисленного типа при этом должна быть выбрана первая группа конструкторов.И для любого типа с плавающей запятой это должно выбрать вторую группу конструкторов.Ваши исходные два конструктора могут легко привести к двусмысленности между float и unsigned int, если вы пройдете int, например.Для другого конструктора с двумя аргументами вы можете использовать свое решение, если хотите.


Тем не менее, я бы тоже использовал заводскую функцию, потому что, на мой взгляд, выбор типа значения параметра довольно хрупок.Большинство людей ожидали бы, что следующий результат будет равен

PointList p(floor(1.5));
PointList u((int)1.5);

Но это привело бы к другому положению дел.

Другие советы

Почему бы не использовать фабричные методы вместо конструкторов?Заводские методы обладают преимуществом настраиваемых имен.


static PointList createNearestValues(unsigned int maxvals) {}
static PointList createByDistance(float maxdist) {}

Рассмотрите возможность использования истинные определения типов.Это требует немного больше усилий со стороны вашего клиентского кода, но корректность вам гарантирована.

Вызовите pointList(10) для первого и pointList(10f) для второго.

Для второго вы также можете использовать версию 10.0.

Если присутствуют конструкторы # 1 и # 2, будет вызван правильный конструктор, если вставляемое вами значение имеет значение float или int и преобразование не должно выполняться.Поэтому просто убедитесь, что вы сделали типы чисел, которые вы используете для вызова, явными (т.е.1f и 1).Конструктор # 3, похоже, не очень подходит, поскольку на самом деле в нем нет необходимости и он просто запутал бы пользователей вашего кода.Если вам нужны значения по умолчанию для любого из чисел, вы могли бы использовать

PointList(int max, float max=VALUE)

и

PointList(float max, int max=VALUE)

Снова:это, по-видимому, наносит больше вреда, чем код, с точки зрения удобочитаемости кода.

Это требует хорошего прочтения по Разрешение перегрузки.

Я бы определенно использовал явный конструкторы.В приведенном примере целое число без знака не преобразуется неявно.

class A
{
public:
    explicit A(float f){}
    explicit A(int i){}
};

void test(){
    unsigned int uinteger(0);
    A a1(uinteger);        //Fails, does not allow implicit conversions

    A a2((float)uinteger); //OK, explicit conversion

    float f(0.0);
    A a3(f);               //OK

    int integer(0);
    A a4(integer);         //OK
}

Сообщение об ошибке достаточно легко понять:

: error C2668: 'A::A' : ambiguous call to overloaded function
: could be 'A::A(int)'
: or       'A::A(float)'
Лицензировано под: CC-BY-SA с атрибуция
Не связан с StackOverflow
scroll top