В C++11 неявное преобразование
#include <string>
struct String
{
template<typename T> operator T*() { return 0; }
operator std::string() { return ""; }
};
int main()
{
String myStr;
std::string str1(myStr); // ambiguous, error C2668
std::string str2 = myStr; // error C2440:
// 'initializing' : cannot convert from 'String' to
// `std::basic_string<char,std::char_traits<char>,std::allocator<char>>',
// No constructor could take the source type,
// or constructor overload resolution was ambiguous
const std::string& rStr = myStr; // Ok, but why?
}
Я использую VS 2013.
вопросы:
почему определения
str1иstr2приводят к различным ошибкам компиляции?как я знаю, когда
rStrсоздается, сначала создается временный строковый объект, затемrStrбудет относиться к временному. Но почему создание временного объекта не приводит к ошибке компиляции? Есть ли разница междуtmpиstrN?
1 ответов
первое определение, std::string str1(myStr); действительно двусмысленно:
std::string str1(myStr.operator char*());
// or
std::string str1(myStr.operator std::string());
таким образом, эта инициализация терпит неудачу из-за неоднозначности.
это по существу тот же сценарий, что и
void foo(char const*);
void foo(std::string);
foo(myStr); // ambiguous
требуется ровно одно пользовательское преобразование, затем будет вызвана функция (для первого определения функция является конструктором). Оба преобразования жизнеспособны, и ни одно из них не является подмножеством другого, поэтому оба имеют одинаковый ранг.
в второе определение, std::string str2 = myStr; на самом деле штраф в размере. Только одно пользовательское преобразование to std::string разрешено, либо через конструктор, либо через функцию преобразования, а не оба. Так только std::string str2 = myStr.operator std::string(); жизнеспособна.
Примечание string str2 = expr;, когда expr не типа string требует expr на преобразовано в std::string. Полученное временное значение затем используется для инициализации str2 через копирования/перемещения:
string str2 = string(expr);
// ~~~~~~ implicit
таким образом, преобразование с правой стороны необходимо преобразовать непосредственно to std::string, в противном случае вам понадобится цепочка из двух пользовательских преобразований для инициализации временного: (UDC = Пользовательское преобразование)
string str2 = string(expr);
// resolved as:
string str2 = expr.operator string(); // fine: one implicit UDC
string str2 = string(expr.operator char*()); // error: two UDCs
например, expr to char const* через operator char* и затем в std::string через конструктор преобразования требуется цепочка из двух пользовательских преобразований => нежизнеспособна. Если мы попытаемся использовать operator char*() преобразование, нам нужен дополнительный конструктор подразумевается вызов конструктора, чтобы сделать RHS a string.
это отличается от string str1( expr ), где expr не нужно преобразовывать имплицитно to string. Это может должны быть преобразованы для инициализации параметра строку конструктор. Прямая инициализация str1 С возможно преобразованный expr не является (N неявным) преобразованием, а просто вызовом функции. Нет дополнительных временных создано:
string str1( expr );
// resolved as:
string str1( expr.operator string() ); // fine
string str1( expr.operator char* () ); // fine
это второе определение отклоняется при компиляции С включено расширение языка. Без языковых расширений эта инициализация прекрасна в обновлении 2 VS2013.
третий следует другой схеме инициализации. Насколько я могу судить, он должен вести себя как второй в этом случае. Кажется, что расширения языка применяются только ко второму, но не к третьему.