Boost logo

Boost :

From: Howard Hinnant (hinnant_at_[hidden])
Date: 1999-11-29 10:43:41


Beman Dawes wrote on 11/29/99 9:43 AM
>There are two ways to provide call_traits:
>
>* A call_traits template which does nothing else. To use, write
>"call_traits<T>::type". Advantage: simple and to-the-point. But if
>traits classes proliferate, doing separate specializations for user
>defined types becomes painful.
>
>* As part of a more general type_traits template. To use, write
>"type_traits<T>::call_type". Advantage: centralizes traits issues.
>But as Darin Adler pointed out, could be come a "slippery slope"
>leading to all sorts of marginally useful garbage.
>
>My gut feeling is to stick with a separate call_traits template. But
>if anyone thinks otherwise, now would be a good time to speak up.

I think seperate. But if type_traits exists, I think call_traits would
be better implemented by making use of type_traits. This would avoid
"yet another list" of bool, char, short, ...

For now, that's an implementation detail that can probably be ignored.

I've been exploring uses of call_traits for something like:

template <typename Iterator>
bool
foo(Iterator i, call_traits<Iterator>::type j)
{
     // do something...
}

I've been plugging in both small and large iterators using both pass by
value and pass by reference and studing the resulting assembly
(optimizations on).

I've only looked at a couple of cases but what I'm seeing so far is that
the following implementation would be just as good:

template <typename Iterator>
bool
foo(Iterator i, const Iterator& j)
{
     // do something...
}

That is, for small (and fast) types the compiler is optimizing pass by
value and pass by const reference to be the same code.

For example consider std::copy implemented simply as:

template <class InputIterator, class OutputIterator>
inline
OutputIterator
copy(InputIterator first, InputIterator last, OutputIterator result)
{
        for (; first != last; ++first, ++result)
                *result = *first;
        return result;
}

and the HelloWorld:

#include <algorithm>
#include <vector>
#include <iostream>

int main()
{
        std::vector<int> v1(10, 5);
        std::vector<int> v2(10);
        std::copy(v1.begin(), v1.end(), v2.begin());
        std::cout << v2[0];
}

For the copy call the following PPC assembly is generated:

        std::copy(v1.begin(), v1.end(), v2.begin());
0000005C: 8001005C lwz r0,92(SP)
00000060: 80A10060 lwz r5,96(SP)
00000064: 5400103A slwi r0,r0,2
00000068: 80610054 lwz r3,84(SP)
0000006C: 7C850214 add r4,r5,r0
00000070: 48000014 b *+20 ; $00000084
00000074: 80050000 lwz r0,0(r5)
00000078: 38A50004 addi r5,r5,4
0000007C: 90030000 stw r0,0(r3)
00000080: 38630004 addi r3,r3,4
00000084: 7C052040 cmplw r5,r4
00000088: 4082FFEC bne *-20 ; $00000074

Changing copy so that the 2nd argument passes last by const reference
instead of by value produces:

        std::copy(v1.begin(), v1.end(), v2.begin());
0000005C: 8001005C lwz r0,92(SP)
00000060: 80A10060 lwz r5,96(SP)
00000064: 5400103A slwi r0,r0,2
00000068: 80810054 lwz r4,84(SP)
0000006C: 7C650214 add r3,r5,r0
00000070: 48000014 b *+20 ; $00000084
00000074: 80050000 lwz r0,0(r5)
00000078: 38A50004 addi r5,r5,4
0000007C: 90040000 stw r0,0(r4)
00000080: 38840004 addi r4,r4,4
00000084: 7C051840 cmplw r5,r3
00000088: 4082FFEC bne *-20 ; $00000074

No significant difference.

So I'm now looking for an example where pass by (const) value (for a
small and fast type) is demonstrably superior to pass by const reference.

-Howard


Boost list run by bdawes at acm.org, gregod at cs.rpi.edu, cpdaniel at pacbell.net, john at johnmaddock.co.uk