Reputation: 6616
Suppose I have a function unique_count
that can count the number of unique elements in any unsorted range:
template<typename InputIt>
std::size_t unique_count(InputIt first, InputIt last)
{
using T = typename std::iterator_traits<InputIt>::value_type;
std::vector</* ??? */> v(first, last);
std::sort(v.begin(), v.end());
return std::distance(v.begin(),
std::unique(v.begin(), v.end()));
}
I create a new vector
because I don't want to modify the original container, but I also don't want to do unnecessary copying. The easiest solution is to make a vector of pointers, but a problem is *first
may return either a const T&
(e.g., from a vector
), or a T
(e.g., from a stream).
The /* ??? */
part should therefore be conceptually equivalent to const T&
. We can't really do this, since vector
s can't hold references, which are unassignable. We can't use reference_wrapper<const T>
either, because it's not constructible from temporaries.
What alternatives do I have?
Upvotes: 0
Views: 70
Reputation: 119184
You want /* ??? */
to be a type that may or may not own a T
object, depending on whether it was constructed with a temporary T
or a const T&
. For obvious reasons, such types should generally be avoided.
One alternative is to use tag dispatch or SFINAE to select one of two possible implementations, depending on whether *first
returns a reference or non-reference type.
Another alternative is to narrow your algorithm a bit so that it only accepts forward iterators. Here you are guaranteed to be able to simply store iterators themselves and use them to compare the elements pointed to:
template <typename ForwardIt>
std::size_t unique_count(ForwardIt first, ForwardIt last) {
std::vector<ForwardIt> v;
for (auto i = first; i != last; ++i) {
v.push_back(i);
}
const auto pred = [](ForwardIt a, ForwardIt b) { return *a < *b; };
std::sort(v.begin(), v.end(), pred);
return std::distance(v.begin(), std::unique(v.begin(), v.end(), pred));
}
Upvotes: 2