Selection of partial specialization blocked by namespace confusion

K

Kai-Uwe Bux

Hi folks,


I observed something that puzzles me. When I do

namespace xxx { using std::swap; }

it appears that xxx::swap and std::swap are not strictly equivalent. In
particular, I think that my implementation will only choose the partial
specialization for std::vector when std::swap is used.

Consider the following code (ignore the part implementing the timing):

#include <ctime>
#include <iostream>
#include <utility>
#include <string>

class timer {
private:

std::clock_t ticks;

public:

timer ( void )
: ticks ( std::clock() )
{}

std::clock_t passed ( void ) const {
return( std::clock() - ticks );
}

double seconds ( void ) const {
return( double( this->passed() ) / CLOCKS_PER_SEC );
}

}; // timer

class print_timer {
private:

std::eek:stream & o_str;
std::string banner;
timer watch;

public:

print_timer ( std::eek:stream & str, std::string msg = std::string() )
: o_str ( str )
, banner ( msg )
, watch ()
{}

~print_timer ( void ) {
o_str << banner << " " << watch.seconds() << "sec" << '\n';
}

}; // print_timer

#define PRINT_TIME print_timer print_timer_dummy


namespace xxx { using std::swap; }

#include <vector>


typedef std::vector< long unsigned > IntVector;
int main ( void ) {
{
IntVector a ( 20000000, 20000000 );
IntVector b ( 20000000, 20000000 );
{
PRINT_TIME( std::cout, "xxx::swap: " );
xxx::swap( a, b );
}
{
PRINT_TIME( std::cout, "std::swap: " );
std::swap( a, b );
}
{
PRINT_TIME( std::cout, "std::swap: " );
std::swap( a, b );
}
{
PRINT_TIME( std::cout, "xxx::swap: " );
xxx::swap( a, b );
}
{
PRINT_TIME( std::cout, "std::swap: " );
std::swap( a, b );
}
}
}


On my machine, this prints:

news_group> a.out
xxx::swap: 0.31sec
std::swap: 0sec
std::swap: 0sec
xxx::swap: 0.33sec
std::swap: 0sec


The different times strongly indicate that std::swap uses a partial
specialization for vectors whereas xxx::swap does not. Is that standard? If
so, why?


Best

Kai-Uwe Bux
 
A

Alf P. Steinbach

* Kai-Uwe Bux:
I observed something that puzzles me. When I do

namespace xxx { using std::swap; }

it appears that xxx::swap and std::swap are not strictly equivalent. In
particular, I think that my implementation will only choose the partial
specialization for std::vector when std::swap is used.

It seems you forgot to #include <vector>. Formally that would be reason
enough, I think, for odd behavior, and as a fix. However I don't understand
how the particular behavior you observed could come about.

With MSVC 7.1 the partial specialization for std::vector is used in both cases
(says the debugger), when <vector> is #include'd; try

#include <iostream>
#include <ostream>
#include <vector>

namespace xxx
{
using std::swap;
}

int main()
{
typedef std::vector<int> V;
std::cout << static_cast<void(*)(V&,V&)>( &std::swap ) << std::endl;
std::cout << static_cast<void(*)(V&,V&)>( &xxx::swap ) << std::endl;

//V a, b;
//std::swap( a, b );
//xxx::swap( a, b );
}
 
K

Kai-Uwe Bux

Alf said:
* Kai-Uwe Bux:

It seems you forgot to #include <vector>. Formally that would be reason
enough, I think, for odd behavior, and as a fix. However I don't
understand how the particular behavior you observed could come about.

Well I did include <vector> (below the timer part), but it appears that
order does matter: if I do

#include <vector>
namespace xxx { using std::swap; }

the specialization is used and when I do

namespace xxx { using std::swap; }
#include <vector>

it is not. Is that formally correct?

With MSVC 7.1 the partial specialization for std::vector is used in both
cases (says the debugger), when <vector> is #include'd; try

#include <iostream>
#include <ostream>
#include <vector>

namespace xxx
{
using std::swap;
}

int main()
{
typedef std::vector<int> V;
std::cout << static_cast<void(*)(V&,V&)>( &std::swap ) << std::endl;
std::cout << static_cast<void(*)(V&,V&)>( &xxx::swap ) << std::endl;

//V a, b;
//std::swap( a, b );
//xxx::swap( a, b );
}

prints
1
1

but what does that tell me? (Sorry for being dense, I also had a look at the
assembler code, but that is all gibberish to me.)


Thanks

Kai-Uwe Bux
 
K

Kai-Uwe Bux

Kai-Uwe Bux said:
Well I did include <vector> (below the timer part), but it appears that
order does matter: if I do

#include <vector>
namespace xxx { using std::swap; }

the specialization is used and when I do

namespace xxx { using std::swap; }
#include <vector>

it is not. Is that formally correct?

I have verified this now:


#include <iostream>

namespace xxx {

template < typename T >
struct container {};

template < typename T >
void print ( T const & ) {
std::cout << "not specialied\n";
}

}

namespace yyy {

using xxx::print;

}

namespace xxx {

template < typename T >
void print ( container<T> const & ) {
std::cout << "specialized for container\n";
}

}


int main ( void ) {
int i = 0;
xxx::container<int> c;

xxx::print(i);
xxx::print(c);

yyy::print(i);
yyy::print(c);
}


prints:

news_group> a.out
not specialied
specialized for container
not specialied
not specialied


As you can see, yyy::print is never specialized. Is that the correct
behavior?


Best

Kai-Uwe Bux
 
A

Alf P. Steinbach

* Kai-Uwe Bux:
Well I did include <vector> (below the timer part), but it appears that
order does matter: if I do

#include <vector>
namespace xxx { using std::swap; }

the specialization is used and when I do

namespace xxx { using std::swap; }
#include <vector>

it is not. Is that formally correct?

The first is formally correct. The second, I don't know, but I think the code
is not correct and the observed behavior can be anything and thus correct.

prints
1
1

but what does that tell me?

That Alf's fingers may produce code that compiles and seemingly works with one
particular compiler, without involving his brain?

The intent was to see whether the std::swap and xxx::swap was the same
function, in which case they'd have the same address.

Says g++:
vc_project.cpp: In function `int main()':
vc_project.cpp:14: warning: the address of `void std::swap(std::vector<_Tp,
_Alloc>&, std::vector<_Tp, _Alloc>&) [with _
_Tp = int, _Alloc = std::allocator<int>]', will always evaluate as `true'

To fix this add a C-cast or reinterpret_cast to void*, i.e. like

(void*)static_cast<void(*)(V&,V&)>( &std::swap )

which is Undefined Behavior or at best Unspecified Behavior (one cannot
officially, portably, cast a function pointer to void*), but hey, it's in the
service of Good.

I should just have let the program _compare_ the two pointers, with Defined
Behavior, and print something like "same function" or "different functions"...
 
M

Michael Kochetkov

Kai-Uwe Bux said:
I have verified this now:

#include <iostream>

namespace xxx {

template < typename T >
struct container {};
template < typename T >
void print ( T const & ) {
std::cout << "not specialied\n";
}
// CCCC
}

namespace yyy {

using xxx::print;
// AAAA
}

namespace xxx {

template < typename T >
void print ( container<T> const & ) {
std::cout << "specialized for container\n";
}
// BBBB
}

int main ( void ) {
int i = 0;
xxx::container<int> c;
xxx::print(i);
xxx::print(c);
A compiler finds both CCCC and BBBB at the first phase of names lookup here
and thinks that there is no argument dependant lookup (ADL) needed. BBBB
is better.
yyy::print(i);
yyy::print(c);
A compiler finds AAAA at the first phase of names lookup here and thinks
that there is no argument dependant lookup (ADL) needed. But it sees only
CCCC at the point of yyy::print declaration (AAAA).
}
prints:

news_group> a.out
not specialied
specialized for container
not specialied
not specialied
As you can see, yyy::print is never specialized. Is that the correct
behavior?
I believe yes. Let us consider an example with ADL which may help to understand
the "elegance" of C++ language:

#include <iostream>


namespace yyy {
template <class T1>
void f1(const T1 t1) {
print(t1);
}
}

namespace xxx {

template < typename T >
struct container {};

template < typename T >
void print ( T const & ) {
std::cout << "not specialied\n";
}
// YYYY

template < typename T >
void print ( container<T> const & ) {
std::cout << "specialized for container\n";
}
// ZZZZ

}


int main ( void ) {
int i = 0;
xxx::container<int> c;

xxx::print(i);
xxx::print(c);

//yyy::f1(i);
// HHHH

yyy::f1(c);
// GGGG
}

A compiler can find nothing at GGGG at the first phase of names lookup and
performs ADL then. YYYY and ZZZZ are both found. Great. But HHHH will never
work because the fundumental types are not bound to a namespace and xxx::print(int)
cannot be found by ADL.
 
K

Kai-Uwe Bux

Michael Kochetkov wrote:

[highly appreciated long and detailed explanation pointing out where and
when ADL kicks in snipped]

I am not sure about the ADL thing -- quite honestly, it is somewhat a
mystery to me. But I figured from a note in 7.3.3/9 that there is a
difference in class templates and function templates. Therefore, the
following code finds the partial specializations regardless of order:

#include <iostream>

namespace xxx {

template < typename T >
struct container {};

template < typename T >
struct print_traits {

static
void act ( void ) {
std::cout << "not specialized\n";
}

};

template < typename T >
void print ( T const & ) {
print_traits<T>::act();
}

}

namespace yyy { using xxx::print; }

namespace xxx {

template < typename T >
struct print_traits < container<T> > {

static
void act ( void ) {
std::cout << "specialized for container\n";
}

};

}

int main ( void ) {
int i = 0;
xxx::container<int> c;

xxx::print(i);
xxx::print(c);

yyy::print(i);
yyy::print(c);
}

prints:
not specialized
specialized for container
not specialized
specialized for container
n


Do you happen to know why ADL is done for class templates and not for
function templates?


Again, many thanks.


Best

Kai-Uwe Bux
 
M

Michael Kochetkov

Michael said:
[highly appreciated long and detailed explanation pointing out where
and when ADL kicks in snipped]

I am not sure about the ADL thing -- quite honestly, it is somewhat a
mystery to me. But I figured from a note in 7.3.3/9 that there is a
I understand it the following way: ADL is performed when a function name
cannot be found by the ordinary lookup. It is probably not quite strict but
shall suit a practicing C++ programmer. More over I start to beging to believe
that ADL shall be preffered for names resolutions while designing large systems.
It may considerably improve the reliability of a system -- the less declarations
a compiler see the better. And of course one shall be happy if a compiler
will always look for a function name that resides in a namespace of it's
arguments. Just my opinion.
difference in class templates and function templates. Therefore, the
following code finds the partial specializations regardless of order:
There is no partial specialization of function templates in C++ language.
That shall explain the case you are talking about.
#include <iostream>

namespace xxx {

template < typename T >
struct container {};
template < typename T >
struct print_traits {
static
void act ( void ) {
std::cout << "not specialized\n";
}
};

template < typename T >
void print ( T const & ) {
print_traits<T>::act();
}
}

namespace yyy { using xxx::print; }
If you had a specialization of print somewhere below a compiler will not
consider it because it will not be a partial specialization of the print.
And no ADL will take place because the function is found pretty well with
the ordinary lookup. But print_traits<T>::act(); call shall consider partial
specializations of print_traits. Again, no ADL.

[...]
Do you happen to know why ADL is done for class templates and not for
function templates?
I have mentioned ADL and have given an example for completeness only. There
cannot be an ADL in you case. You cannot write:
namespace yyy { using xxx::print; }
or
namespace yyy { using print; }

having not declared xxx::print somewhere before. The proper functions _are_
found the ordinary way in your case. And a compiler just picks up the most
suitable one. But it chooses it from the functions that were found before
the point of namespace yyy { using xxx::print; } declaration.
You may use parameterised classes instead of functions indeed. It would make
life a bit of easier. But you might want to consider the ADL approach I have
mentioned above -- functions declarations are not visible and a compeler
looks for them in the namespaces of threir arguments. And of course you would
have use unqualified functions' names in this case.
 

Ask a Question

Want to reply to this thread or ask your own question?

You'll need to choose a username for the site, which only take a couple of moments. After that, you can post your question and our members will help you out.

Ask a Question

Members online

No members online now.

Forum statistics

Threads
473,997
Messages
2,570,241
Members
46,831
Latest member
RusselWill

Latest Threads

Top