Template specialization

I'm trying to provide a specialization of std::begin & std::end for my custom class. I am still unable to find what's the proper syntax:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
#include <iterator>
#include <vector>

struct U{
private:
    std::vector<int> u;
public:
    U() : u {1, 2, 3}{ }

    decltype(u)::iterator start(){ return u.begin(); }

    decltype(u)::iterator finish(){ return u.end(); }
};

namespace std {
    template<>
    auto begin<U>(U& u) -> decltype(u.start()){
        return u.start();
    }
}


results in the error "template-id ‘begin<U>’ for ‘std::vector<int>::iterator std::begin(U&)’ does not match any template declaration"

I derived the above code considering the signature
1
2
3
4
template<class _Container>
  inline auto
  begin(_Container& __cont) -> decltype(__cont.begin())
  { return __cont.begin(); }

and assuming you can always make a specialization just by replacing _Container with your custom struct.

Thanks,
Dean
> I'm trying to provide a specialization of std::begin & std::end for my custom class

For a custom class (which does not have members begin or end), place the non-member functions begin() and end() in the same namespace as the class (or in an associated namespace).
... otherwise, begin-expr and end-expr are begin(__range) and end(__range), respectively, where begin and end are looked up in the associated namespaces [Note: Ordinary unqualified lookup is not performed. —end note ]


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
#include <iterator>
#include <vector>
#include <iostream>

namespace A {

    struct U {
        
    private:
        std::vector<int> u;
    public:
        U() : u {1, 2, 3}{ }

        decltype(u)::iterator start(){ return u.begin(); }

        decltype(u)::iterator finish(){ return u.end(); }
    };

    auto begin( U& u ) { return u.start() ; }
    auto end( U& u ) { return u.finish() ; }
}

int main()
{
    A::U u ;
    for( auto v : u ) std::cout << v << '\n' ;
}

http://coliru.stacked-crooked.com/a/311d4ec3934f2f3e
Hi JLBorges,

thank you for the reply. Being aware of ADL, I understand this is the proper solution. Still, I don't see why the specialization of the template cannot be performed inside the namespace std. There must be a syntax that allows this, I just don't get which one is.

Thanks,
Dean
An explicit specialisation of namespace std { template< class C > auto begin( C& c ) -> decltype(c.begin()); }
for some type U would be: namespace std { template<> auto begin<U>( U& u ) -> decltype(u.begin()); }

The LLVM compiler emits semantically rich diagnostics for this kind of error:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
template < typename T > auto foo( T& t ) -> decltype( t.bar() ) ; //

struct A { int bar() { return 0 ; } };
// this is a valid explicit specialisation.
template <> auto foo<A>( A& a ) -> decltype( a.bar() ) ;

struct B { int not_bar() { return 0 ; } };
// this is not a valid explicit specialisation.
template <> auto foo<B>( B& b ) -> decltype( b.not_bar() ) ;
// LLVM: note: candidate template ignored: substitution failure [with T = B]: no member named 'bar' in 'B'
// LLVM: template < typename T > auto foo( T& t ) -> decltype( t.bar() ) ; //
// LLVM:                              ^                          ~~~

struct C { double not_bar() { return 0 ; } int bar() { return 0 ; } };
// this is not a valid explicit specialisation.
template <> auto foo<C>( C& c ) -> decltype( c.not_bar() ) ;
// LLVM: note: candidate template ignored: could not match 'auto (C &) -> decltype(t.bar())' against 'auto (C &) -> decltype(c.not_bar())' 

http://coliru.stacked-crooked.com/a/95321daf025da6b6

Also see: 'Why Not Specialize Function Templates?' http://www.gotw.ca/publications/mill17.htm
Hi JLBorges,

thank you for the detailed examples, also the article was an appropriate point. So the part with decltype( x ) also needs to be exactly matched. From a language perspective it looks to me as a draconian limitation, I wonder whether allowing that would have introduced some other issues?

Thanks,
Dean
> From a language perspective it looks to me as a draconian limitation

Not really; function templates can be overloaded.

A function template can be overloaded either by (non-template) functions of its name or by (other) function templates of the same name. ... The complete set of candidate functions includes all the synthesized declarations and all of the non-template overloaded functions of the same name. - IS


1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
#include <iostream>

template < typename T > auto foo( T& t ) -> decltype( t.bar() )
{ std::cout << "template: unspecialised\n" ; return t.bar() ; }

struct A { int bar() { return 0 ; } };
template <> auto foo<A>( A& a ) -> decltype( a.bar() )
{ std::cout << "template: specialisation for 'A' (here too, favour overloading)\n" ; return 0 ;  }

struct B { void not_bar() {} };
auto foo( B& b ) -> decltype( b.not_bar() ) { std::cout << "overload for 'B'\n" ; }

struct C { double not_bar() { return 0 ; } int bar() { return 0 ; } };
auto foo( C& c ) -> decltype( c.not_bar() ) { std::cout << "overload for 'C'\n" ; return 0 ; }

int main()
{
    struct X { void bar() {} };

    X x ; foo(x) ; // template: unspecialised
    A a ; foo(a) ; // template: specialisation for 'A' (here too, favour overloading)
    B b ; foo(b) ; // overload for 'B'
    C c ; foo(c) ; // overload for 'C'
}

http://coliru.stacked-crooked.com/a/d2ab8f5989eaefa6


> I wonder whether allowing that would have introduced some other issues?

The key to understanding this is simple, and here it is: Specializations don't overload. ... Overload resolution only selects a base template (or a nontemplate function, if one is available).
...
The rationale for why specializations don't participate in overloading is simple, once explained, because the surprise factor is exactly the reverse: The standards committee felt it would be surprising that, just because you happened to write a specialization for a particular template, that it would in any way change which template gets used. Under that rationale, and since we already have a way of making sure our version gets used if that's what we want (we just make it a function, not a specialization), we can understand more clearly why specializations don't affect which template gets selected.

- Sutter 'Why Not Specialize Function Templates?' http://www.gotw.ca/publications/mill17.htm
Topic archived. No new replies allowed.