Как неявно специализировать преобразование?

В следующем коде, если я попытаюсь преобразовать массив шаблонов в 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: изменение его с константного на неконстантный не имеет заметного эффекта. Возможно, стоит отметить, что std::string имеет три разные функции преобразования, одна из которых принимает массив символов, а другая — std::string.   -  person SSight3    schedule 07.10.2011


Ответы (2)


Во-первых, причина двусмысленности: вы предоставляете и конверсию в char*, и конверсию в std::string const, а std::string нравится и то, и другое.

Кстати, прежде чем перейти к вашему вопросу, const в operator std::string const когда-то было хорошей идеей, которую отстаивали, например. Скотта Мейерса, но в настоящее время не годится: мешает эффективному движению.

В любом случае, повторите вопрос, просто избегайте неявных преобразований. Сделайте эти преобразования явными. Теперь я ответил на это в ответ на другой вопрос SO, и кто-то (я полагаю, что человек троллит) прокомментировал, что С++ 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.
}

Ура и чт.

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 предотвращает операции перемещения. Раньше люди использовали swaptimization, чтобы получить эту эффективность. Операции перемещения заставляют компилятор делать это автоматически. - 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