Я бы не сказал, что он предпочитает столько, сколько требуется.

я есть классC у которого есть оператор приведения к чему угодно. В примере я попытался привести его кstd::string тремя разными способами:static_castконструкторstd::string и присваиваяstd::string, Однако компилируется только последний, в то время как другие вызывают ошибку неоднозначного конструктора.

Причина ошибки достаточно ясна: существует много способов конвертацииC к чему-то, что конструкторstd::string могу принять. Но в чем разница между этими случаями? Почему оператор приведения работает здесь, как задумано, а не там?

struct C {
    template<typename T>
    operator T() const {
        return T{};
    }
};

int main() {
    C c;

    cout << static_cast<string>(c) << endl; // compile error
    string bad(c); // compile error
    string ok = c; // compiles successfully
}

UPD: как упомянул в комментариях Болов, эта проблема не воспроизводится с C ++ 17. Я протестировал его с g ++ - 5 и clang-3.8 с -std = c ++ 11 и -std = c ++ 14, и он показывает описанные ошибки.

 Ivan Smirnov14 окт. 2017 г., 05:19
@vsoftco Нет. Если вы добавитеoperator const char* также двусмысленность возвращается. Нет шаблонов сейчас.
 bolov14 окт. 2017 г., 05:00
хм .. интересно
 bolov14 окт. 2017 г., 04:56
не могу воспроизвести:godbolt.org/g/ESR8cw
 Ivan Smirnov14 окт. 2017 г., 04:58
@bolov как ни странно, но он не воспроизводится в C ++ 17. Воспроизводится в Godbolt с -std = c ++ 11. Я добавлю это в пост, спасибо.
 vsoftco14 окт. 2017 г., 05:00
@bolov Не компилирует gcc / clang на моей стороне, см. например.Вот, И да, действительно, g ++ -std = c ++ 17 принимает код.

Ответы на вопрос(1)

Решение Вопроса

До C ++ 17

static_cast<string>(c) а такжеstring bad(c) выполняетпрямая инициализация, тогда

конструкторыT рассматриваются и наилучшее соответствие выбирается по разрешению перегрузки. Затем вызывается конструктор для инициализации объекта.

Как вы сказали, все возможные конструкторыstd::string рассматриваются иC может быть преобразован во что угодно, то вызывает двусмысленность.

string ok = c выполняетинициализация копии (обратите внимание, это не назначение), затем

ЕслиT является типом класса, а cv-неквалифицированная версия типаother не являетсяT или полученный изT, или еслиT это неклассный тип, но типother это тип класса, определяемые пользователем последовательности преобразования, которые могут преобразовывать из типаother вT (или для типа, полученного изT еслиT является типом класса и доступна функция преобразования), и выбирается лучший из них с помощью разрешения перегрузки.

Это означает, что преобразование изC вstd::string рассматривается и используется для инициализации здесь.

После C ++ 17

Начиная с C ++ 17 дляпрямое инициирование,

если инициализатор является выражением prvalue, у которого тип cv-unqualified является тем же классом, что иTсамо выражение инициализатора, а не временное материализованное из него, используется для инициализации целевого объекта: см.копия elision (начиная с C ++ 17)

Это означает, что преобразование изC вstd::string предоставляется и используется для инициализации, затем неопределенность исчезает, и код работает хорошо.

ЖИТЬ

 chris14 окт. 2017 г., 05:23
Чтобы уточнить инициализацию копии ([over.ics.user] / 3):Если определяемое пользователем преобразование определяется специализацией шаблона функции преобразования, вторая стандартная последовательность преобразования должна иметь ранг точного соответствия. Я бы не сказал, что он предпочитает столько, сколько требуется.

Ваш ответ на вопрос