libstdc++ (the standard library the gcc uses by default) applies precisely this optimisation:
117 /**
118 * Destroy a range of objects. If the value_type of the object has
119 * a trivial destructor, the compiler should optimize all of this
120 * away, otherwise the objects' destructors must be invoked.
121 */
122 template<typename _ForwardIterator>
123 inline void
124 _Destroy(_ForwardIterator __first, _ForwardIterator __last)
125 {
126 typedef typename iterator_traits<_ForwardIterator>::value_type
127 _Value_type;
128 std::_Destroy_aux<__has_trivial_destructor(_Value_type)>::
129 __destroy(__first, __last);
130 }
And the specialisation of std::_Destroy_aux
for __has_trivial_destructor(_Value_type) == true
:
109 template<>
110 struct _Destroy_aux<true>
111 {
112 template<typename _ForwardIterator>
113 static void
114 __destroy(_ForwardIterator, _ForwardIterator) { }
115 };
I would expect that most other well-written standard library implementations do the same.