In this Q&A I wrote a little wrapper class that provides reverse iterator access to a range, relying on the c++1z language feature template argument deduction for class templates (p0091r3, p0512r0)
#include <iostream>
#include <iterator>
#include <vector>
template<class Rng>
class Reverse
{
Rng const& rng;
public:
Reverse(Rng const& r) noexcept
:
rng(r)
{}
auto begin() const noexcept { using std::end; return std::make_reverse_iterator(end(rng)); }
auto end() const noexcept { using std::begin; return std::make_reverse_iterator(begin(rng)); }
};
int main()
{
std::vector<int> my_stack;
my_stack.push_back(1);
my_stack.push_back(2);
my_stack.puhs_back(3);
// prints 3,2,1
for (auto const& elem : Reverse(my_stack)) {
std::cout << elem << ',';
}
}
However, doing a nested application of Reverse
does not yield the original iteration order
// still prints 3,2,1 instead of 1,2,3
for (auto const& elem : Reverse(Reverse(my_stack))) {
std::cout << elem << ',';
}
Live Example (same output for g++ 7.0 SVN and clang 5.0 SVN)
The culprit seems to be the template argument deduction for class templates because the usual wrapper function does allow for correct nesting
template<class Rng>
auto MakeReverse(Rng const& rng) { return Reverse<Rng>(rng); }
// prints 1,2,3
for (auto const& elem : MakeReverse(MakeReverse(my_stack))) {
std::cout << elem << ',';
}
Live Example (same output for g++ and clang)
Question: is nested template argument deduction for class templates supposed to work only "one level" deep, or is this a bug in the current implementations of both g++ and clang?
my_stack
is a named variable, and the reference is stored inside theReverse
object, so what is dangling here? – VincennesReverse(my_stack)
is not a named varible, and you store a reference to it insideReverse(Reverse(my_stack))
. – Bulldozestd::tuple t(std::tuple(1));
-- shouldt
betuple<tuple<int>>
or a copy oftuple<int>(1)
? (I think the latter) – MusquashReverse<Reverse<std::vector<int>>>(Reverse<std::vector<int>>(my_stack))
instead, I get a runtime error; probably bescause of dangling reference. – Readestd::pair
so that you don't have to use themake_pair
function helper – Vincennesconst&
so that should bind the temporary, right? – Vincennesrng
member by value doesn't fix this – VincennesReverse
is a copy constructor and therefore uses the same template argument. – ReadeReverse(Reverse(my_stack))
or evenReverse(Reverse(Reverse(Reverse(my_stack))))
is still a copy of a singleReverse(my_stack)
– Musquashstd::tuple t(std::tuple(1));
yieldsstd::tuple<int>
, notstd::tuple<std::tuple<int>>
, which I think is correct behavior, but I have no reference to prove this – Musquash