views:

77

answers:

2

I'm hitting a compile error in VS2010 with code that compiles cleanly in VS2008.

Here's my error, from the Output Window, with all of its verbosity:

...\elementimpl.h(49): error C2668: 'std::basic_string<_Elem,_Traits,_Ax>::basic_string' : ambiguous call to overloaded function
      with
      [
          _Elem=char,
          _Traits=std::char_traits<char>,
          _Ax=std::allocator<char>
      ]
      c:\program files (x86)\microsoft visual studio 10.0\vc\include\xstring(700): could be 'std::basic_string<_Elem,_Traits,_Ax>::basic_string(std::basic_string<_Elem,_Traits,_Ax> &&)'
      with
      [
          _Elem=char,
          _Traits=std::char_traits<char>,
          _Ax=std::allocator<char>
      ]
      c:\program files (x86)\microsoft visual studio 10.0\vc\include\xstring(590): or       'std::basic_string<_Elem,_Traits,_Ax>::basic_string(const _Elem *)'
      with
      [
          _Elem=char,
          _Traits=std::char_traits<char>,
          _Ax=std::allocator<char>
      ]
      while trying to match the argument list '(CXStr)'

and here's the code in error, elementimpl.h(49):

    std::string getAttributeValue(const char* attr) { return getAttribute(CXStr(attr)); }

getAttribute() returns a CXStr (an ADT), which obviously no std::string constructor accepts, so it needs to convert the CXStr to something suitable.

CXStr can be converted to either a std::string or a char*:

class CXStr
{
public:
    ...
    CXStr(std::string);
    CXStr(const char* ch);

    const CXStr& operator=(const char*);
    const CXStr& operator=(std::string&);

    operator char*();
    operator std::string(); 
    ...
}

When I run through the debugger in VS2008, I can see that it goes through "CXStr::operator std::string()" after getAttribute(), and then it goes through the std::string copy constructor to create the object to be returned by getAttributeValue(). I want my 2010 build to behave the same way.

My questions:

  • What is VS2010 doing differently, and why?
  • What code change can I make to fix this error? Some things that work, but are unsatisfactory:
    1. commenting out "CXStr::operator char*()"; unsatisfactory because some other code uses this conversion.
    2. explicitly calling "getAttribute(...).operator std::string()" at elementimpl.h(49); unappealing because this same error occurs in 27 different places and it seems like there must be a more centralized way to fix it.
A: 

Why haven't you tried commenting out operator std::string() yet?

You could also explicitly cast your CXStr to std::string.

Frankly though, I can't see the use of this CXStr thing. What, if anything, does it provide in the way of services?

Noah Roberts
Like I said, as built in VS2008, "CXStr::operator std::string()" is the chosen conversion path, and I want to preserve that behavior, so eliminating that operator seems a non-option for me.
I tried changing elementimpl.h(49) to "std::string getAttributeValue(const char* attr) { return static_cast<std::string>(getAttribute(CXStr(attr))); }", but the compiler complained: "...\elementimpl.h(49): error C2440: 'static_cast' : cannot convert from 'CXStr' to 'std::string'[\n]No constructor could take the source type, or constructor overload resolution was ambiguous"
There's much more to CXStr than the snippet I pasted in the question. Its primary use is conversion to and from Xerces-C++-compatible strings (with elements of type XMLCh*, which is UTF-16).
Here's the thing...obviously you're going to have to change something. I realize that nobody likes change, but it's inevitable so just get used to it. Now that you've been smacked around for having implicit conversion operators you might learn from that experience and think twice next time.
Noah Roberts
I'm working in maintenance mode. I didn't write this code. I'm only supporting it. Obviously I'm going to have to change something, I recognize that. I'm just trying to understand what's going on here, and ensure that the change I go with is The Right Thing to do. Your tone isn't very constructive.
+3  A: 

The problem is, that VS2010 added move constructors and your implementation falls directly into this hole.

Previously there was only one constructor, the const char* one, which was available to the std::string constructor. Now there is the move constructor, too.

I really think a design relying on implicit conversions is dangerous. Thats why all one parameter constructors should be explicit ...

I think you should discourage the use of implicit operators, because they lead to bad overloads being used. If you cannot do that, wrap the currently failing calls into an explicit conversion call std::string( CXStr( x ).toString() ). There can be other errors which are hidden by using 'non-explicit' cast operators ... .

Christopher