Копирование между буферами разного размера, шаблонными по их фиксированному размеру

#c #templates

#c #шаблоны

Вопрос:

Упрощенная версия будет выглядеть примерно так:

 template<int Size> struct IntBuffer {int items[Size];}

IntBuffer<32> b1;
IntBuffer<16> b2;


b1 = b2; // I want this to be allowed as the size of b1 >= b2
b2 = b1; // I want this to be disallowed as the size of b2 < b1
  

РЕДАКТИРОВАТЬ: кажется, я должен быть более ясным… Я хочу вызвать ошибку времени компиляции, если назначение запрещено из-за упомянутого поведения ограничения размера. Также без boost и идеально подходит для компиляторов, которые могут не иметь полной поддержки C 11, такой как MSVC 2010.

Комментарии:

1. Реализуйте конструктор копирования / перегруженный оператор присваивания и применяйте свою логику внутри.

2. Добавьте шаблон оператора присваивания с static_assert внутренним (требуется поддержка C 11).

3. Интересно, почему эти комментарии не являются ответами…

4. Извините, я должен был упомянуть в идеале что-то дружественное до полной поддержки C 11

5. static_assert поддерживается в VS2010.

Ответ №1:

Сначала позвольте мне сказать, что мне не кажется интуитивно понятным, что вы должны это делать. Если я прочитаю код и увижу это, мне будет интересно, что случилось с ДРУГИМИ элементами, если размер целевого буфера больше размера исходного буфера (будут ли они сохранены? Будут ли они очищены? Будут ли они изменены каким-либо другим недетерминированным способом?). Не только это, но и если вы ограничите его буферами одинакового размера, сгенерированный компилятором конструктор копирования и оператор присваивания копирования будут просто РАБОТАТЬ, никакой дополнительной работы с вашей стороны не требуется.

Но если после всего этого вы все еще хотите это сделать, вы можете создать свой собственный оператор присваивания копирования. Вам придется написать свой собственный static_assert (но вы, конечно, можете сделать это на C 98, поскольку boost сделал это), поскольку вы явно исключили два места, которые я знаю, чтобы получить то, что уже написано и отлажено для вас (C 11 и boost).

Оператор может выглядеть примерно так:

 template <int Size> template <int RightSize>
IntBuffer<Size>amp; IntBuffer<Size>::operator=(const IntBuffer<RightSize>amp; right)
{
    static_assert(RightSize <= Size);
    // The real work.
}
  

Комментарии:

1. Это правильный ответ, если бы я не понимал, как вы можете это сделать до C 11. Но это была моя вина, что я не указал это в начале извините! Что касается фактического варианта использования, это немного сложнее, поэтому все не так плохо, как вы опасаетесь 😉

2. Почему ваш оператор шаблонизирован дважды? (Для меня это похоже на реализацию в классе, где первого шаблона не должно быть.)

3. Должно быть template <int Size, int RightSize> Я думаю, просто опечатка?

4. @wb. Нет Size , это параметр шаблона класса. Как только создается экземпляр шаблона класса, Size он фиксируется и, следовательно, больше не является параметром шаблона для фактического оператора. Только если вы реализуете функции вне шаблона класса, вам нужно добавить параметры шаблона класса в дополнение к параметрам шаблона функции, отсюда и двойной шаблон. Но тогда IntBuffer<Size>:: во фрагменте кода отсутствует.

5. @wb Нет, это оператор присваивания шаблона шаблонного класса, я просто IntBuffer<Size>:: случайно остановился там. Спасибо @leemes за указание на мою ошибку.

Ответ №2:

Одна из возможностей заключается в перегрузке оператора присваивания шаблоном, который имеет размер другого буфера в качестве параметра. В реализации вы можете static_assert выполнить свои требования, если вы можете использовать C 11:

 template<int OtherSize>
IntBuffer<Size> amp; operator=(const IntBuffer<OtherSize> amp; other) {
    static_assert(OtherSize <= Size, "Error assigning to IntBuffers of different size: The size of the right hand side buffer shouldn't be larger than the one on the left hand side.");
    // (implementation)
}
  

Если вы не можете использовать эту функцию C 11 (вам не нужен полный C 11) из-за ограничений компилятора, вы также можете использовать подход на основе SFINAE, но в этом случае вы не можете выдавать описательные сообщения об ошибках:

 template<int OtherSize>
typename enable_if<OtherSize <= Size, IntBuffer<Size> amp;>::type
//                 ^---------------^  ^---------------^
//                    requirement        return type
operator=(const IntBuffer<OtherSize> amp;other) {
    // (implementation)
}
  

где enable_if просто копия возможной реализации std::enable_if , если вы используете pre-C 11:

 template<bool B, class T = void>
struct enable_if {};

template<class T>
struct enable_if<true, T> { typedef T type; };
  

(источник: http://en.cppreference.com/w/cpp/types/enable_if )

Демонстрация: http://ideone.com/uXGo5E

Комментарии:

1. Мой мозг подводит меня к подходу SFINAE — я обновил вопрос, чтобы упомянуть идеальную поддержку до C 11 — я виноват, что не упомянул об этом.

2. Вы можете имитировать static_assert до C 11, специализируя шаблон, который принимает bool.

3. Ах, хорошо, я думаю, теперь я понимаю это … спасибо.

4. @leemes: я почти уверен, что должен отметить, что ответ спасибо… просто просматриваю это с кофе…

5. Мне нужен кофе покрепче, так как мне потребовалось время, чтобы понять, что я думаю, что условие в вашем примере неправильное. Я думаю, это должно быть: enable_if..