template member functions

Hi, I'm reading "Memory and Resources part of "C++ Programming Language" and there is this example about std::bitset and its template member function
1
2
3
s = bs.to_string<C,Tr,A>(c0,C1)    s[i]=(b[i])?c1:c0; s is a basic_string<C,Tr,A>
s = bs.to_string<C,Tr,A>(c0)       s=bs.template to_string<C,Tr,A>(c0,C{'1'})
s = bs.to_string<C,Tr,A>()         s = bs.template to_string<C,Tr,A>(C{'0'}, C{'1'})

But later on its used like this
1
2
3
4
5
6
7
8
9
void binary(int i)
{
    std::bitset<8*sizeof(int)> b = i;

    cout << b.to_string<char, char_traits<char>, allocator<char>>() << '\n'; //general and verbose
    cout << b.to_string<char>() << '\n';  //use default traits and allocator
    cout << b.to_string<>() << '\n';      //use all defaults
    cout << b.to_string() << '\n';        //use all defaults
}


Why template keyword is used in none of those examples if I read that it has to be used in case member functions are templates?
for example cout << b.template to_string<char>() << '\n';

If all of these examples are the same function except only some are using default arguments why did

1st and 2nd example worked with or without template keyword?
3rd example didn't work at all with or without template keyword?
4th example only worked without template keyword?


I haven't read yet "Templates The Complete Guide" yet so maybe there will be explanation.
Last edited on
I read that it has to be used in case member functions are templates?
Where did you read that? That's not true. Honestly, I don't even think it's syntactically correct.
Probably I misunderstood something from this

Naming a member template after a .(dot), -> or :: requires similar use of the keyword template
(similar use to typename)

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
class Pool  //some allocator
{
public:
    template<typename T> T* get();
    template<typename T> void release(T*);
    //...
}

template<typename Alloc>
void f(Alloc& all)
{
    int* p1 = all.get<int>();         //syntax error : get is assumed to name a non-template
    int* p2 = all.template get<int>();  //OK: get() is assumed to be an template
}

void user(Pool& pool)
{
    f(pool);
    //...
}


I suppose this will get very clear to me after that "Template : The Complete Guide" right?
> Naming a member template after a .(dot), -> or :: requires similar use of the keyword template

Only in a template definition, for a dependant name, if it is not part of the current instantiation.

in a template definition, a dependent name that is not a member of the current instantiation is not considered to be a template name unless the disambiguation keyword template is used or unless it was already established as a template name.
...
the template prefix is allowed even if the name is not dependent or (since C++11) the use does not appear in the scope of a template.
http://en.cppreference.com/w/cpp/language/dependent_name


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
#include <iostream>
#include <string>

struct A
{
    template < typename C = char, typename T = std::char_traits<C>, typename A = std::allocator<C> >
    std::basic_string<C,T,A> bar() const { return {} ; }
};

template < typename X > auto foobar( const X& x ) // for instantiation: foobar<A>
{
     return x.bar<>() ; // error: template disambiguator is required for the dependent name
     
                        // error: use 'template' keyword to treat 'bar' as a dependent template name - LLVM (excellent, understands and tells us what the problem really is) 
                        // error: expected primary-expression before '>' token, expected primary-expression before ')' token - GNU (poor, mechanically generated dumb diagnostic)
                        // note: Microsoft (without the v140_clang_3_7 toolchain) chokes on the template disambiguator for dependent template names (bad, non-conforming)
}

int main()
{
    A a ;

    a.bar() ; // fine: bar< char, std::char_traits<char>, std::allocator<char> >
    a.template bar() ; // fine: bar< char, std::char_traits<char>, std::allocator<char> >

    a.bar<>() ; // fine: bar< char, std::char_traits<char>, std::allocator<char> >
    a.template bar<>() ; // fine: bar< char, std::char_traits<char>, std::allocator<char> >

    a.bar<char16_t>() ; // fine: bar< char16_t, std::char_traits<char16_t>, std::allocator<char16_t> >
    a.template bar<wchar_t>() ; // fine: : bar< wchar_t, std::char_traits<wchar_t>, std::allocator<wchar_t> >

    a.bar< char, std::char_traits<char>, std::allocator<char> >() ; // fine
    a.template bar< char, std::char_traits<char>, std::allocator<char> >() ; // fine

    foobar(a) ;
}

http://coliru.stacked-crooked.com/a/075b691c4acda1e6
Thank you man once again! Very nicely explained :)

Just 1 extra question about the code - arent there supposed to be
->decltype(x.template bar()) after the
template < typename X > auto foobar( const X& x )

It worked both ways but in my book decltype is used if return type is auto and actual return type is unknown (coz it depends on X).

Don't get me wrong I'm not complaining about the answer :D
Support for auto without a trailing return type specifier was added in C++14:
in this case, the return type is deduced from (the type of the expression in) the return statement(s).
Thanks man :)
Topic archived. No new replies allowed.