c ++ 11 — Почему неявное преобразование в C ++ работает, а явное — нет?

Следующий код успешно компилируется в C ++ 11:

#include "json.hpp"using json = nlohmann::json ;

using namespace std ;

int main(){
json js = "asd" ;
string s1 = js ; // <---- compiles fine
//string s2 = (string)js ; // <---- does not compile
}

Это включает JSON для современного C ++. Рабочий пример в этот wandbox.

Переменная JSON js неявно преобразуется в строку. Однако, если я раскомментирую последнюю строку, которая является явным преобразованием, она не будет скомпилирована. Результаты компиляции Вот.

Помимо особых нюансов этой библиотеки json, как вы кодируете класс, чтобы неявное преобразование работало, а явное — нет?
Есть ли какой-то квалификатор конструктора, который допускает такое поведение?

7

Решение

Вот упрощенный код, который воспроизводит ту же проблему:

struct S
{
template <typename T>
operator T() // non-explicit operator
{ return T{}; }
};

struct R
{
R() = default;
R(const R&) = default;
R(R&&) = default;
R(int) {} // problematic!
};

int main()
{
S s{};
R r = static_cast<R>(s); // error
}

Мы видим, что ошибка компиляции похожа:

error: call of overloaded 'R(S&)' is ambiguous
R r = static_cast<R>(s);
^
note: candidates...
R(int) {}
R(R&&) = default;
R(const R&) = default;

Проблема зависит от общего S::operator T(), который с радостью вернет значение любому типу, который вы хотите. Например, назначение s для любого типа будет работать:

int i = s; // S::operator T() returns int{};
std::string str = s; // S::operator T() returns std::string{};

T выводится на тип конверсии. В случае std::string, у него много конструкторов, но если вы сделаете копирования инициализации (1) формы object = other, T выводится на тип левого объекта (который std::string).

Кастинг это другое дело. Видите, это та же проблема, если вы пытаетесь инициализировать копию, используя третью форму (которая в данном случае прямая инициализация):

R r(s); // same ambiguity error

Хорошо, каковы перегрузки конструктора для R снова?

R() = default;
R(const R&) = default;
R(R&&) = default;
R(int) {}

При условии RКонструкторы могут взять либо другое R, или же intпроблема становится очевидной, так как система вывода типа шаблона не знает, какой из них является правильным ответом из-за контекста, из которого вызывается оператор. Здесь прямая инициализация должна учитывать все возможные перегрузки. Вот основное правило:

является типом, который требуется в результате преобразования. п тип возврата шаблона функции преобразования

В этом случае:

R r = s;

R является типом, который требуется в результате преобразования (). Тем не менее, вы можете сказать, какой тип будет представлять в следующем коде?

R r(s);

Теперь контекст имеет R а также int в качестве параметров, потому что в R есть конструктор, который принимает целые числа. Но тип преобразования должен быть выведен только для одного из них. R является действительным кандидатом, так как есть по крайней мере один конструктор, который принимает R, int также является допустимым кандидатом, поскольку есть конструктор, принимающий также целое число. Нет победителя, поскольку оба они одинаково действительны, отсюда и неоднозначность.

Когда вы приведете свой объект JSON к std::string, ситуация точно такая же. Есть конструктор, который принимает строку, и есть другой, который принимает распределитель. Обе перегрузки допустимы, поэтому компилятор не может выбрать одну.

Проблема исчезла бы, если бы оператор преобразования был помечен как explicit, Это означает, что вы сможете сделать std::string str = static_cast<std::string>(json)Однако вы теряете способность неявно конвертировать его как std::string str = json,

8

Другие решения

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

string s1 = js

он исключает из перегрузки все конструкторы и преобразования, помеченные как «явные», поэтому в результате выбирается одна функция.
Вместо этого, когда компилятор нашел:

string s2 = (string)js ;

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

2

По вопросам рекламы ammmcru@yandex.ru
Adblock
detector