2

With the following code, if I attempt to convert a template array to std::string, instead of the compiler using the expected std::string conversion method, it raises an ambiguity resolution problem (as it attempts to call the array conversion methods):

#include <iostream>

template<typename TemplateItem>
class TestA
{
    public:
        TemplateItem Array[10];

        operator const TemplateItem *() const {return Array;}
        operator const std::string() const;
};

template<>
TestA<char>::operator const std::string() const
{
    std::string Temp("");
    return Temp;
}

int main()
{
    TestA<char> Test2;
    std::string Temp("Empty");
    Temp = Test2; //Ambiguity error. std::string or TemplateItem * ? 
    return 0;
}

What modification do I need to make to the code in order to make it so the code correctly and implicitly resolve to the std::string conversion function? Especially given the const TemplateItem * would be treated as a null-terminated array (which it won't likely be).

Xavier Holt
  • 14,471
  • 4
  • 43
  • 56
SE Does Not Like Dissent
  • 1,767
  • 3
  • 16
  • 36
  • as `operator std::string` returns by value, it doesn't need to be `const std::string`. Does that get rid of the problem? – Mooing Duck Oct 06 '11 at 21:34
  • @MooingDuck: Changing it from const to non-const has no notable effect. It might be worth noting std::string has three different conversion functions, one of which takes a char array, the other a std::string. – SE Does Not Like Dissent Oct 06 '11 at 21:37

2 Answers2

5

First, the reason you have ambiguity: you provide both conversion to char* and conversion to std::string const, and std::string likes them both.

By the way, before getting to your question, the const in operator std::string const was once a good idea, advocated by e.g. Scott Meyers, but is nowadays ungood: it prevents efficient moving.

Anyway, re the question, just avoid implicit conversions. Make those conversions explicit. Now I answered that in response to another SO question, and someone (I believe the person was trolling) commented that C++98 doesn't support explicit type conversion operators. Which was true enough, technically, but pretty stupid as a technical comment. Because you don't need to use the explicit keyword (supported by C++11), and indeed that's not a good way to make the conversions explicit. Instead just name those conversions: use named member functions, not conversion operators.


#include <iostream>

template<typename TemplateItem>
class TestA
{
    public:
        TemplateItem Array[10];

        TemplateItem  const* data() const { return Array; }
        std::string str() const;
};

template<>
std::string TestA<char>::str() const
{
    return "";
}

int main()
{
    TestA<char> test2;
    std::string temp( "Empty" );
    temp = test2.str();     // OK.
    temp = test2.data();    // Also OK.
}

Cheers & hth.

Cheers and hth. - Alf
  • 142,714
  • 15
  • 209
  • 331
  • Do you have a source which explains const being inefficient? It would be interesting to read. – Pubby Oct 06 '11 at 21:40
  • A demo of a named conversion operator wouldn't go amis. – Mooing Duck Oct 06 '11 at 21:40
  • @Pubby8: `const` prevents move operations. Previously people used "swaptimization" to get that efficiency. Move operations make the compiler do it automatically. – Mooing Duck Oct 06 '11 at 21:40
  • @Mooing Duck: there is no such thing as a "named conversion operator". However, named conversions are plenty of in the standard library. E.g. `std::string::c_str()`, or `std::ostringstream::str()`. – Cheers and hth. - Alf Oct 06 '11 at 21:42
  • I would have logically thought that std::string would have used operational precedence for one of it's own type over calling a templated function. – SE Does Not Like Dissent Oct 06 '11 at 21:45
  • @AlfP.Steinbach: Fine. I used the wrong word. I even gave the answer a +1. It would still be better with a bit of sample code to clarify how to do conversion properly as per the third paragraph. – Mooing Duck Oct 06 '11 at 21:47
  • @SSight3: as far a C++ is concerned, nope. All assignment operators are just functions, and all equal. (Almost. It prefers `const` functions to non-`const` functions, but if your assignment operator is `const` you should be kept away from code) – Mooing Duck Oct 06 '11 at 21:48
  • @SSight3: at the place of the assignment there is no templating left anywhere, all is already concrete, instantiated. So the two possible conversions are on a completely equal footing. – Cheers and hth. - Alf Oct 06 '11 at 21:50
  • @MooingDuck: I meant more in the case of [conversion operator precedent](http://stackoverflow.com/questions/1092714/conversion-precedence-in-c) – SE Does Not Like Dissent Oct 06 '11 at 22:01
  • Are saying that implicit conversion operators are generally bad or did I misread? – John Dibling Oct 06 '11 at 22:47
0

I will add, after thinking about it, here's the reasoning and what should be done:

The operator const TemplateItem *(); Should be deleted.

Why? There never will be an instance where you would want implicit conversion of TestA (or any template class) to a TemplateItem array with an unknown size - because this has absolutely no bounds checking (array could be sizeof 1 or 10 or 1000 or 0) and would likely cause a crash if a calling function or class received the array with no idea what size it is.

If the array is needed, either use the operator[] for direct items, or GetArray (which would signal the user intends to pass an unknown-length array).

Retain operator const std::string. Code will compile. Possible memory issues down the line averted.

(Alf for his efforts has been selected as the 'correct' answer, although this is the more logical option)

SE Does Not Like Dissent
  • 1,767
  • 3
  • 16
  • 36