Шаблон с нецелыми и сдвигами [дубликат]

Класс String и классы-оболочки имеют различную реализацию методов equals() и hashCode(), чем класс Object. Метод equals () класса Object сравнивает ссылки объектов, а не содержимое. Метод hashCode () класса Object возвращает отдельный хэш-код для каждого отдельного объекта, независимо от его содержимого.

Это приводит к проблеме, когда вы используете коллекцию Map, а ключ имеет тип Persistent, тип StringBuffer / builder. Поскольку они не переопределяют equals () и hashCode () в отличие от класса String, equals () возвращает false, когда вы сравниваете два разных объекта, хотя оба имеют одинаковое содержимое. Он заставит hashMap хранить одинаковые ключи содержимого. Сохранение одинаковых ключей содержимого означает, что оно нарушает правило Карты, поскольку Map не позволяет дублировать ключи вообще. Поэтому вы переопределяете методы equals (), а также методы hashCode () в своем классе и предоставляете реализацию (IDE может генерировать эти методы), чтобы они работали так же, как equals () и hashCode () и не допускали одинаковые ключи содержимого.

Вы должны переопределить метод hashCode () вместе с equals (), потому что equals () работает по hashcode.

Кроме того, переопределение метода hashCode () вместе с equals () помогает унаследовать контракт equals () - hashCode (): «Если два объекта равны, то они должны иметь одинаковый хеш-код».

Когда вам нужно написать специальную реализацию для hashCode ()?

Как мы знаем, внутренняя работа HashMap основана на принципе Hashing. Существуют определенные ведра, в которых хранятся записи. Вы настраиваете реализацию hashCode () согласно вашему требованию, чтобы объекты той же категории могли храниться в одном индексе. при сохранении значений в коллекции карт с использованием метода put(k,v), внутренняя реализация put ():

put(k, v){
hash(k);
index=hash & (n-1);
}

означает, что он генерирует индекс и индекс генерируется на основе хэш-кода конкретного ключа объект. Поэтому сделайте этот метод генерирующим hashcode согласно вашему требованию, потому что те же записи hashcode будут сохранены в одном и том же ведре или индексе.

Вот и все!

29
задан Sheljohn 13 June 2015 в 13:44
поделиться

4 ответа

TL; DR

Существует несколько способов получить различное поведение во время выполнения, зависящее от параметра шаблона. Производительность не должна быть вашей главной задачей здесь, но гибкость и ремонтопригодность должны быть. Во всех случаях различные тонкие обертки и постоянные условные выражения будут оптимизированы на любом достойном компиляторе для создания релизов. Ниже небольшое резюме с различными компромиссами (вдохновлено на этот ответ на @AndyProwl).

Время выполнения, если

Ваше первое решение - это простое время выполнения if:

template<class T>
T numeric_procedure(const T& x)
{
    if (std::is_integral<T>::value) {
        // valid code for integral types
    } else {
        // valid code for non-integral types,
        // must ALSO compile for integral types
    }
}

Это просто и эффективно: любой достойный компилятор будет оптимизировать мертвую ветвь.

Есть несколько недостатков:

  • на некоторых платформах (MSVC), постоянное условное выражение дает ложное предупреждение компилятора, которое вам тогда нужно игнорировать или замолчать.
  • Но хуже, что на всех соответствующих платформах обе ветви оператора if/else должны действительно компилироваться для всех типов T, даже если одна из ветвей, как известно, не должна быть взята. Если T содержит разные типы элементов в зависимости от его характера, тогда вы получите ошибку компилятора, как только вы попытаетесь получить к ним доступ.

Отправка тегов

второй подход известен как диспетчеризация тегов:

template<class T>
T numeric_procedure_impl(const T& x, std::false_type)
{
    // valid code for non-integral types,
    // CAN contain code that is invalid for integral types
}    

template<class T>
T numeric_procedure_impl(const T& x, std::true_type)
{
    // valid code for integral types
}

template<class T>
T numeric_procedure(const T& x)
{
    return numeric_procedure_impl(x, std::is_integral<T>());
}

Он работает нормально, без накладных расходов во время выполнения: временный std::is_integral<T>() и вызов однострочной вспомогательной функции будут оптимизированы на любая достойная платформа.

Недостатком основного (младшего IMO) является то, что у вас есть шаблон с 3 вместо 1 функции.

SFINAE

Тесно связанный с отправкой тегов - SFINAE (Ошибка замены не является ошибкой)

template<class T, class = typename std::enable_if<!std::is_integral<T>::value>::type>
T numeric_procedure(const T& x)
{
    // valid code for non-integral types,
    // CAN contain code that is invalid for integral types
}    

template<class T, class = typename std::enable_if<std::is_integral<T>::value>::type>
T numeric_procedure(const T& x)
{
    // valid code for integral types
}

Это имеет тот же эффект, что и отправка тегов, но работает несколько иначе. Вместо того, чтобы использовать аргумент-вывод для выбора надлежащей вспомогательной перегрузки, он непосредственно манипулирует набором перегрузки для вашей основной функции.

Недостатком является то, что он может быть хрупким и сложным способом, если вы точно не знаете что весь набор перегрузки (например, с тяжелым кодом шаблона, ADL может вывести больше перегрузок из связанных пространств имен, о которых вы не думали). И по сравнению с диспетчеризацией меток выбор на основе чего-либо другого, кроме бинарного решения, намного больше задействован.

Частичная специализация

Другой подход заключается в использовании вспомогательного элемента шаблона класса с функцией оператор приложения и частично специализируется на нем

template<class T, bool> 
struct numeric_functor;

template<class T>
struct numeric_functor<T, false>
{
    T operator()(T const& x) const
    {
        // valid code for non-integral types,
        // CAN contain code that is invalid for integral types
    }
};

template<class T>
struct numeric_functor<T, true>
{
    T operator()(T const& x) const
    {
        // valid code for integral types
    }
};

template<class T>
T numeric_procedure(T const& x)
{
    return numeric_functor<T, std::is_integral<T>::value>()(x);
}

Это, вероятно, самый гибкий подход, если вы хотите иметь мелкозернистый контроль и минимальное дублирование кода (например, если вы также хотите специализироваться на размере и / или выравнивании , но говорят только для типов с плавающей точкой). Соответствие шаблонов, заданное с помощью частичной специализированности шаблонов, идеально подходит для таких сложных задач.

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

Если constexpr (предложение C ++ 1z)

Это reboot неудачных ранее предложений для static if (которые используются на языке программирования D)

template<class T>
T numeric_procedure(const T& x)
{
    if constexpr (std::is_integral<T>::value) {
        // valid code for integral types
    } else {
        // valid code for non-integral types,
        // CAN contain code that is invalid for integral types
    }
}

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

Concepts-Lite (предложение C ++ 1z)

Concepts-Lite представляет собой предстоящую техническую спецификацию , которая должна быть частью следующей крупной версии C ++ (C ++ 1z, с z==7 в качестве лучшей догадки).

template<Non_integral T>
T numeric_procedure(const T& x)
{
    // valid code for non-integral types,
    // CAN contain code that is invalid for integral types
}    

template<Integral T>
T numeric_procedure(const T& x)
{
    // valid code for integral types
}

Этот подход заменяет ключевое слово class или typename внутри скобок template< > названием понятия, описывающим семейство типов, для которых должен работать код. Это можно рассматривать как обобщение методов диспетчеризации меток и SFINAE. Некоторые компиляторы (gcc, Clang) имеют экспериментальную поддержку этой функции. Прилагательное Lite ссылается на неудачное предложение Concepts C ++ 11.

45
ответ дан Community 26 August 2018 в 11:27
поделиться
2
ответ дан Cheers and hth. - Alf 26 August 2018 в 11:27
поделиться

Подтвердить @MooingDuck и @Casey

template<class FN1, class FN2, class ...Args>
decltype(auto) if_else_impl(std::true_type, FN1 &&fn1, FN2 &&, Args&&... args)
{
    return fn1(std::forward<Args>(args)...);
}

template<class FN1, class FN2, class ...Args>
decltype(auto) if_else_impl(std::false_type, FN1 &&, FN2 &&fn2, Args&&... args)
{
    return fn2(std::forward<Args>(args)...);
}

#define static_if(...) if_else_impl(__VA_ARGS__, *this)

И так же просто:

static_if(do_it,
    [&](auto& self){ return 1; },
    [&](auto& self){ return self.sum(2); }
);

Работает как static if - компилятор go только для «истинной» ветви.


PS Вы должны иметь self = *this и делать из него вызовы членов, из-за ошибки gcc . Если у вас есть вложенные лямбда-вызовы, вы не можете использовать this-> вместо self.

1
ответ дан tower120 26 August 2018 в 11:27
поделиться

Обратите внимание, что хотя оптимизатор может быть способен обрезать статически известные тесты и недостижимые ветви из сгенерированного кода, компилятор все еще должен иметь возможность компилировать каждый

То есть:

int foo() {
  #if 0
    return std::cout << "this isn't going to work\n";
  #else
    return 1;
  #endif
}

будет работать нормально, потому что препроцессор удаляет мертвую ветвь до того, как компилятор увидит ее, но:

int foo() {
  if (std::is_integral<double>::value) {
    return std::cout << "this isn't going to work\n";
  } else {
    return 1;
  }
}

не будет. Несмотря на то, что оптимизатор может отбросить первую ветвь, она все равно не скомпилируется. Здесь используется справка enable_if и SFINAE, потому что вы можете выбрать допустимый (компилируемый) код, а неверный (несовместимый) код «Сбой компиляции не является ошибкой».

11
ответ дан Useless 26 August 2018 в 11:27
поделиться
Другие вопросы по тегам:

Похожие вопросы: