Как да специализирам имплицитно преобразуването?

Със следния код, ако се опитам да преобразувам шаблонен масив в std::string, вместо компилаторът да използва очаквания метод за преобразуване на std::string, той поражда проблем с разрешаването на двусмислието (тъй като се опитва да извика методите за преобразуване на масива) :

#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;
}

Каква модификация трябва да направя в кода, за да го направя така, че кодът да се разрешава правилно и имплицитно към функцията за преобразуване на std::string? Особено като се има предвид, че const TemplateItem * ще се третира като масив с нулев край (което вероятно няма да бъде).


person SSight3    schedule 06.10.2011    source източник
comment
тъй като operator std::string връща по стойност, не е необходимо да бъде const std::string. Това премахва ли проблема?   -  person Mooing Duck    schedule 07.10.2011
comment
@MooingDuck: Промяната му от const на non-const няма забележим ефект. Може да си струва да се отбележи, че std::string има три различни функции за преобразуване, едната от които приема char масив, а другата std::string.   -  person SSight3    schedule 07.10.2011


Отговори (2)


Първо, причината, поради която имате неяснота: осигурявате както преобразуване към char*, така и преобразуване към std::string const, а std::string ги харесва и двете.

Между другото, преди да стигнем до вашия въпрос, const в operator std::string const някога беше добра идея, застъпена от напр. Скот Майерс, но днес е недобър: пречи на ефективното движение.

Както и да е, по въпроса, просто избягвайте имплицитни преобразувания. Направете тези реализации изрични. Сега отговорих на това в отговор на друг SO въпрос и някой (вярвам, че човекът троли) коментира, че C++98 не поддържа оператори за преобразуване на тип explicit. Което беше достатъчно вярно технически, но доста глупаво като технически коментар. Тъй като не е необходимо да използвате ключовата дума explicit (поддържана от C++11) и това наистина не е добър начин да направите реализациите изрични. Вместо това просто именувайте тези преобразувания: използвайте именувани членски функции, а не оператори за преобразуване.


#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.
}

Наздраве & hth.

person Cheers and hth. - Alf    schedule 06.10.2011
comment
Имате ли източник, който обяснява, че const е неефективен? Би било интересно да се прочете. - person Pubby; 07.10.2011
comment
Демонстрация на наименуван оператор за преобразуване не би била в полза. - person Mooing Duck; 07.10.2011
comment
@Pubby8: const предотвратява операциите по преместване. Преди това хората използваха суоптимизация, за да постигнат тази ефективност. Операциите за преместване карат компилатора да го прави автоматично. - person Mooing Duck; 07.10.2011
comment
@Mooing Duck: няма такова нещо като наименуван оператор за преобразуване. Въпреки това, наименуваните реализации са много в стандартната библиотека. напр. std::string::c_str() или std::ostringstream::str(). - person Cheers and hth. - Alf; 07.10.2011
comment
Логично бих си помислил, че std::string би използвал оперативен приоритет за един от собствените си типове пред извикването на шаблонна функция. - person SSight3; 07.10.2011
comment
@AlfP.Steinbach: Добре. Използвах грешната дума. Дори дадох +1 на отговора. Все още би било по-добре с малко примерен код, за да изясните как да направите преобразуването правилно според третия параграф. - person Mooing Duck; 07.10.2011
comment
@SSight3: що се отнася до C++, не. Всички оператори за присвояване са просто функции и всички равни. (Почти. Предпочита const функции пред не-const функции, но ако вашият оператор за присвояване е const, трябва да се държите далеч от код) - person Mooing Duck; 07.10.2011
comment
@SSight3: на мястото на присвояването никъде не е останал шаблон, всичко вече е конкретно, инстанцирано. Така че двете възможни преобразувания са напълно равностойни. - person Cheers and hth. - Alf; 07.10.2011
comment
@MooingDuck: Имах предвид повече в случая с прецедент на оператор за преобразуване - person SSight3; 07.10.2011
comment
Искате да кажете, че имплицитните оператори за преобразуване като цяло са лоши или съм прочел погрешно? - person John Dibling; 07.10.2011

Ще добавя, след като помислих, ето мотивите и какво трябва да се направи:

Операторът const TemplateItem *(); Трябва да се изтрие.

Защо? Никога няма да има случай, в който бихте искали имплицитно преобразуване на TestA (или който и да е шаблонен клас) в масив TemplateItem с неизвестен размер - защото това няма абсолютно никаква проверка на граници (масивът може да бъде sizeof 1 или 10 или 1000 или 0) и вероятно ще доведе до срив, ако извикваща функция или клас получи масива без представа какъв е размерът му.

Ако масивът е необходим, или използвайте оператора[] за директни елементи, или GetArray (което би сигнализирало, че потребителят възнамерява да предаде масив с неизвестна дължина).

Запазване на оператора const std::string. Кодът ще се компилира. Предотвратени са възможни проблеми с паметта.

(Алф за неговите усилия е избран като „правилен“ отговор, въпреки че това е по-логичният вариант)

person SSight3    schedule 06.10.2011