Why is the zero-argument constructor necessary?

Sorry to post this long example but I am having a hard time reproducing the issue when I try to simplify it. This is a fairly tricky piece of code, thanks in advance to anyone who makes the effort to look into it!

The problem is specifically at line 99, the compilation error is :

error: no matching function for call to ‘wrapper<std::forward_list<int> >::wrapper()’
     reverser<wrapper<C>,1>::reverser(* _collection) {}


I could perhaps just add a zero-argument constructor to the class reverser<T,1> but I don't understand why it is necessary? How should I fix the code?

if you want to test the code, I recommend you first comment out the line auto rf = make_reverser(f); at the bottom of the code. It should compile fine as long as you compile as C++17. The problem is this call to make_reverse(T) when T doesn't expose a reverse iterator.

What the code is trying to do:

The wrapper<T> object wraps a container object T (i.e. container) and creates a vector of pointers to the elements of T.

The reverser<T> object wraps any iterable object (i.e. container or generator) and exposes the reverse iterators of this T object and so reverser<T> iterates in the reverse order of T but the underlying T is not changed.

The trick is that in the case T doesn’t have a reverse iterator, the reverser<T> is built from a wrapper<T> in instead of a T (e.g. the container without reverse iterator is replaced by a wrapper<T>, which does have a reverse iterator).

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
#include <iostream>
#include <stdlib.h>
#include <vector> 
#include <forward_list>
#include <optional>


template<typename T, typename = decltype(std::declval<T>().rbegin(),true)>
constexpr bool  has_reverse_iterator(int x) 
{return true ;} 

template<typename T>
constexpr bool has_reverse_iterator(double x) 
{return false;} 

template<typename T>
constexpr bool has_reverse_iterator() 
{return has_reverse_iterator<T>(1) ;} 


/////////////////////////////////////////////////////////////////////
/////////////////// wrapper
/////////////////////////////////////////////////////////////////////


template< class C > 
class wrapper{
    typedef  decltype(*(std::declval<C>().begin())) R;
    typedef typename std::remove_reference<R>::type V;
    std::vector<V*> collection;
    typedef decltype(collection.begin()) iteratorbase;
    typedef decltype(collection.rbegin()) riteratorbase;
public:

    class iterator:public iteratorbase{
        public:
    iterator(iteratorbase iter):iteratorbase(iter){}


    R& operator*()  {
        iteratorbase & ib = * this;
        return(* * ib);}        
    };
    
    class riterator:public riteratorbase{
        public:
    riterator(riteratorbase iter):riteratorbase(iter){}
        
    R& operator*()  {
        riteratorbase & ib = * this;
        return(* * ib); }        
    };
 
    iterator begin(){ return iterator(collection.begin());}
    iterator end(){ return iterator(collection.end());}         
    riterator rbegin(){ return riterator(collection.rbegin());}
    riterator rend(){ return riterator(collection.rend());}         

 
    wrapper( C & v){
    for(auto& x : v){collection.push_back( &x);}
    }
};

template<typename T>
wrapper<T> make_wrapper(T& v){
    return wrapper<T>(v);
}

/////////////////////////////////////////////////////////////////////
/////////////////// reverser
/////////////////////////////////////////////////////////////////////

template<class C ,int X>  
class reverser;

template<class C>
class reverser<C,1> {
public:

protected:
    C& collection;
    typedef decltype(collection.rbegin()) iterator;
    typedef decltype(*collection.rbegin()) R;

public:
    iterator begin(){return collection.rbegin();}
    iterator end(){ return collection.rend();}
    reverser( C & v): collection(v){}
};

template< class C > 
class reverser<C,2> : public reverser<wrapper<C>,1>{
     wrapper<C> rtype;
     std::optional<wrapper<C>> _collection;
    
public:
    reverser( C & v):_collection(std::move(make_wrapper(v))),
    reverser<wrapper<C>,1>::reverser(* _collection) {}    //<-- this initializer doesn't resolve. 
};

template<typename T,typename std::enable_if<! has_reverse_iterator<T>(),std::nullptr_t>::type = nullptr>
reverser<T,2> make_reverser(T & v){
    return reverser<T,2>(v);
}


template<typename T,typename std::enable_if<has_reverse_iterator<T>(),std::nullptr_t>::type = nullptr>
reverser<T,1> make_reverser(T & v){
    return reverser<T,1>(v);
}


/////////////////////////////////////////////////////////////////////
/////////////////// main
/////////////////////////////////////////////////////////////////////

int main(){
    ////// the case when the base object has a reverse iterator is fine. 
    std::vector<int> v {1,2,3,4,5,6,7,8} ;
    auto rv = make_reverser(v);   //<--- this works
    for(auto i:rv){std::cout<<i<<std::endl;}

    ////// the case when the base object doesn't have a reverse iterator doesn't compile 
    std::forward_list<int> f {1,2,3,4,5,6,7,8} ;
    auto rf = make_reverser(f);   //<--- this doesn't compile
    for(auto i:rf){std::cout<<i<<std::endl;}
    
}
Last edited on
clang is a bit more helpful here:

prog.cc:98:5: error: constructor for 'reverser<std::__1::forward_list<int, std::__1::allocator<int> >, 2>' must explicitly initialize the member 'rtype' which does not have a default constructor
    reverser( C & v):_collection(std::move(make_wrapper(v))),
    ^
prog.cc:104:12: note: in instantiation of member function 'reverser<std::__1::forward_list<int, std::__1::allocator<int> >, 2>::reverser' requested here
    return reverser<T,2>(v);
           ^
prog.cc:126:15: note: in instantiation of function template specialization 'make_reverser<std::__1::forward_list<int, std::__1::allocator<int> >, nullptr>' requested here
    auto rf = make_reverser(f);   //<--- this doesn't compile
              ^
prog.cc:94:17: note: member is declared here
     wrapper<C> rtype;
                ^
Last edited on
Thanks a lot. Problem solved! The clang messages were extremely helpful.
The line wrapper<C> rtype; actually doesn't belong in the code.
At some point, I had used the definition typedef wrapper<C> rtype; but later decided I didn't need it. I knew I had to clean up that line but it was not a priority. However, I kept staring at the code without seeing that the keyword typedef had gone missing so rtype; had become a rogue variable, which screamed for initialization!

Now, I need to check the code does do what I hope it should be doing!
Last edited on
Topic archived. No new replies allowed.