В 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.
третий следует другой схеме инициализации. Насколько я могу судить, он должен вести себя как второй в этом случае. Кажется, что расширения языка применяются только ко второму, но не к третьему.