Re: MSVC++ 2005 Express Ed. build error when mixing boost
lexical_cast and shared_ptr
On Nov 20, 8:45 pm, "Alf P. Steinbach" <al...@start.no> wrote:
* hsmit.h...@gmail.com:
[...]
Here's the code.
#include <boost/lexical_cast.hpp>
#include <iostream>
#include <vector>
#include <string>
//--------------------------------------------------------------
template<class T>
std::ostream & operator<<(std::ostream& s, const std::vector<T> & d) {
typedef typename std::vector<T>::const_iterator iter;
iter it;
for (it = d.begin() ; it != d.end() ; ++it) {
s << *it;
s << "\n";
}
return s;
}
//--------------------------------------------------------------
template<class T>
std::istream & operator>>(std::istream& s, std::vector<T> & d) {
while (!s.eof()) {
char buf[500];
s.getline(buf, sizeof(buf));
d.push_back(buf);
}
return s;
}
//--------------------------------------------------------------
int main (int argc, char ** argv) {
std::vector<std::string> vecstr1;
vecstr1.push_back("hi");
vecstr1.push_back("there");
std::string str = boost::lexical_cast<std::string>(vecstr1);
std::cout << str << std::endl;
return 0;
}
This compiles quite nicely and even spits out the correct output.
This version seems to use just ordinary lookup, not
argument-dependent lookup (ADL).
That's the impression I get, but why? According to =A714.6.4:
In resolving dependent names, names from the following
sources are considered:
-- Declarations that are visible at the point of
definition of the template.
-- Declarations from namespaces associated with the
types of the function arguments both from the
instantiation context (14.6.4.1) and from the
definition context.
The operator<< is obviously dependent (or?). The one he's
interested in is not visible at the point of definition of the
template (in boost/lexical_cast.hpp). And the only namespace
associated with any of the types that I can see is std::, and
the operator he's looking for isn't in that either.
There's definitely something I'm missing here, because his code
compiles with g++ 4.1.0. It's not related to the fact that his
operators are templates, because it compiles even if I modify
the code to use the concrete type std::vector<std::string>. But
I know that g++ implements the above rule, because I've run into
cases where the code wouldn't compile because of it. So what's
different here, compared to, say:
#include <algorithm>
#include <iostream>
#include <iterator>
#include <vector>
std::ostream&
operator<<( std::ostream& dest, std::vector< int > const& src )
{
dest << '[' ;
for ( std::vector< int >::const_iterator it = src.begin() ;
it != src.end() ;
++ it ) {
if ( it != src.begin() ) {
dest << ',' ;
}
dest << *it ;
}
dest << ']' ;
return dest ;
}
int
main()
{
std::vector< std::vector< int > > v ;
for ( int i = 0 ; i < 10 ; ++ i ) {
v.push_back( std::vector< int >() ) ;
for ( int j = 0 ; j < 10 ; ++ j ) {
v.back().push_back( 10* i + j ) ;
}
}
std::copy( v.begin(), v.end(),
std::ostream_iterator< std::vector< int >
( std::cout, "\n" ) ) ;
return 0 ;
}
(which doesn't compile with either g++ or VC++, unless I put the
operator<< in namespace std, which is formally illegal.)
Now for the problem:
If I change the include #includes at the top to:
#include <boost/lexical_cast.hpp>
#include <boost/shared_ptr.hpp>
#include <iostream>
#include <vector>
#include <string>
I get the following compilation error (only the last few lines are
provided for clarity):
lexical_cast.hpp(150) : while compiling class template member function
'bool boost::detail::lexical_stream<Target,Source>::operator <<(const
Source &)'
with
[
Target=std::string,
Source=NewSource
]
lexical_cast.hpp(219) : see reference to class template instantiation
'boost::detail::lexical_stream<Target,Source>' being compiled
with
[
Target=std::string,
Source=NewSource
]
main.cpp(38) : see reference to function template instantiation
'Target boost::lexical_cast<std::string,std::vector<_Ty>>(const Source
&)' being compiled
with
[
Target=std::string,
_Ty=std::string,
Source=std::vector<std::string>
]
lexical_cast.hpp(151) : error C2228: left of '.fail' must have class/
struct/union
[shared_ptr.hpp] brings into play
template<class E, class T, class Y>
std::basic_ostream<E, T>& operator<<(
std::basic_ostream<E, T> & os, shared_ptr<Y> const & p
)
{
os << p.get();
return os;
}
in namespace boost.
I don't know why it's critical that it's in namespace boost (possibly
because the stream used in lexical cast is of a class defined in
namespace boost, engaging ADL in boost),
In the version of Boost I have here (1.33.0), lexical_cast uses
a boost::detail::lexical_stream<>, which in turn uses an
std::basic_stringstream<>. Neither of which are in namespace
boost!
In the end, boost::lexical_cast<std::string>(vecstr1), in his
code, must find his template operator<<. According to my
reading of the standard, it shouldn't, since it should only do
the look-up in the definition context, and in associated
namespaces (std, boost and boost::detail) at the point of
instantiation. Since his operator isn't available at the point
of definition, and isn't in one of the associated namespaces, it
shouldn't be found.
So what am I overlooking. Why is the behavior in his cas
different than that in my sample program, above?
but anyway, with this definition in boost you get the above
error, and with same definition in global namespace it
compiles OK. So it looks like it makes the operator<< call in
lexical_cast ambigious, via ADL. Which is difficult to
understand because SFINAE should throw it out of
consideration?
But even though I can't give you exact answer (is there some
language lawyer present?) I think you get the general drift.
Well, I'd like to hear too from someone who knows how name
lookup in templates really works.
--
James Kanze (GABI Software) email:james.kanze@gmail.com
Conseils en informatique orient=E9e objet/
Beratung in objektorientierter Datenverarbeitung
9 place S=E9mard, 78210 St.-Cyr-l'=C9cole, France, +33 (0)1 30 23 00 34