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

#c #class #templates

#c #класс #шаблоны

Вопрос:

С помощью следующего кода, если я попытаюсь преобразовать массив шаблонов в 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 * будет обрабатываться как массив с нулевым завершением (чего, скорее всего, не будет).

Комментарии:

1. поскольку operator std::string возвращается по значению, это не обязательно const std::string . Устраняет ли это проблему?

2. @MooingDuck: изменение его с const на non-const не имеет заметного эффекта. Возможно, стоит отметить, что std::string имеет три разные функции преобразования, одна из которых принимает массив символов, а другая — std::string .

Ответ №1:

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

Кстати, прежде чем перейти к вашему вопросу, const in 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.
}
  

Приветствия и привет.

Комментарии:

1. У вас есть источник, который объясняет неэффективность const ? Было бы интересно почитать.

2. Демонстрация именованного оператора преобразования не пойдет amis.

3. @Pubby8: const предотвращает операции перемещения. Раньше люди использовали «оптимизацию обмена», чтобы добиться такой эффективности. Операции перемещения заставляют компилятор выполнять это автоматически.

4. @Mooing Duck: не существует такого понятия, как «именованный оператор преобразования». Однако именованных преобразований в стандартной библиотеке предостаточно. Например std::string::c_str() , или . std::ostringstream::str()

5. Я бы логически подумал, что std::string использовал бы операционный приоритет для одного из своих собственных типов по сравнению с вызовом шаблонной функции.

Ответ №2:

Я добавлю, подумав об этом, вот рассуждения и что нужно сделать:

Оператор const TemplateItem *(); Должен быть удален.

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

Если массив необходим, либо используйте operator[] для прямых элементов, либо getArray (что будет сигнализировать о намерении пользователя передать массив неизвестной длины).

Сохранить оператор const std::string . Код будет скомпилирован. Предотвращены возможные проблемы с памятью в будущем.

(Alf за его усилия был выбран в качестве «правильного» ответа, хотя это более логичный вариант)