Управление неявным преобразованием типов в C ++ - PullRequest
2 голосов
/ 27 июня 2009

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

  • Ближайшие N точек к данной точке в пространстве.
  • все точки на данном расстоянии.

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

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

PointList( unsigned int maxvals ); // #1

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

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

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

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

Ответы [ 7 ]

6 голосов
/ 27 июня 2009

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


static PointList createNearestValues(unsigned int maxvals) {}
static PointList createByDistance(float maxdist) {}
5 голосов
/ 27 июня 2009

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

  • Повышение: это преобразование типов, меньших 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);

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

2 голосов
/ 27 июня 2009

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

1 голос
/ 27 июня 2009

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

1 голос
/ 27 июня 2009

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

PointList(int max, float max=VALUE)

и

PointList(float max, int max=VALUE)

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

1 голос
/ 27 июня 2009

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

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

0 голосов
/ 17 сентября 2009

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

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)'
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...