Here is a question asking about how to differentiate fill and range constructors. The code is copied here:
template <typename T>
struct NaiveVector {
vector<T> v;
NaiveVector(size_t num, const T &val) : v(num, val) { // fill
cout << "(int num, const T &val)" << endl;
}
template <typename InputIterator>
NaiveVector(InputIterator first, InputIterator last) : v(first, last) { // range
cout << "(InputIterator first, InputIterator last)" << endl;
}
};
The code above doesn't work, as described in that question. The solution is using SFINAE to define the range constructor, like this example:
template
<
typename InputIterator
, typename = typename ::std::enable_if
<
::std::is_same
<
T &
, typename ::std::remove_const
<
decltype(*(::std::declval< InputIterator >()))
>::type
>::value
, void
>::type
>
NaiveVector(InputIterator first, InputIterator last) : v(first, last)
{
cout << "(InputIterator first, InputIterator last)" << endl;
}
This gist of this solution is comparing the dereferenced type of InputIterator
with T &
. If it is truly an iterator, the comparison of std::is_same
will be true, and the range constructor will be selected; if it is not an iterator, there will be a template substitution failure so the range constructor will be deleted, and hence the fill constructor will be selected.
However, there is a problem for the solution above. If the input InputIterator
is of a const_iterator
type (e.g. cbegin()
), then dereferencing it will yield a const T &
, and its const
-ness cannot be removed by std::remove_const
, so the comparison in std::is_same
will be false, resulting in the range constructor being wrongly deleted.
GNU's C++ STL had a bug possibly (I guess) because of this. In this bug, the method only accept an iterator, but not a const_iterator.
Question:
(1) is there a nicer workaround than combining two std::is_same
conditions with an OR operator, one comparing the dereferenced type with T &
, the other with const T &
?
(2) if the workaround described in (1) is adopted, is std::remove_const
still necessary, now that it cannot remove const
-ness from a reference type, and dereferencing an (const or non-const) iterator will always yield a reference, either const T &
or T &
.
Aucun commentaire:
Enregistrer un commentaire