Итак, у меня есть существующая библиотека, которая предоставляет тип строки.
Он неявно преобразует строки строки стиля C в так:
struct TypeIDoNotOwn {
TypeIDoNotOwn() {}
TypeIDoNotOwn(TypeIDoNotOwn const&) {}
TypeIDoNotOwn(char const*) {}
TypeIDoNotOwn& operator=(TypeIDoNotOwn const&) {return *this;}
TypeIDoNotOwn& operator=(char const*) {return *this;}
operator char const*() const {return nullptr;}
};
у него есть другие методы, но я не думаю, что они важны. У этих методов есть тела, но моя проблема не связана с ними, поэтому я их исчерпал.
Что я хочу сделать, так это создать новый тип, который можно использовать относительно взаимозаменяемо с указанным выше типом и с "raw string constants"
. Я хочу иметь возможность взять экземпляр TypeIDoNotOwn
и заменить его на TypeIDoOwn
, и компилировать код.
В качестве примера этот набор операций:
void test( TypeIDoNotOwn const& x ) {}
int main() {
TypeIOwn a = TypeIDoNotOwn();
TypeIDoNotOwn b;
a = b;
b = a;
TypeIOwn c = "hello";
TypeIDoNotOwn d = c;
a = "world";
d = "world";
char const* e = a;
std::pair<TypeIDoNotOwn, TypeIDoNotOwn> f = std::make_pair( TypeIOwn(), TypeIOwn() );
std::pair<TypeIOwn, TypeIOwn> g = std::make_pair( TypeIDoNotOwn(), TypeIDoNotOwn() );
test(a);
}
Если я заменил TypeIOwn
на TypeIDoNotOwn
выше, он скомпилируется. Как мне его скомпилировать с помощью TypeIOwn
без изменения TypeIDoNotOwn
? И без необходимости вводить какие-либо отбрасывания или изменения, кроме изменения типа в точке объявления?
Моя первая попытка выглядит примерно так:
struct TypeIOwn {
TypeIOwn() {}
operator char const*() const {return nullptr;}
operator TypeIDoNotOwn() const {return {};}
TypeIOwn( TypeIOwn const& ) {}
TypeIOwn( char const* ) {}
TypeIOwn( TypeIDoNotOwn const& ) {}
TypeIOwn& operator=( char const* ) {return *this;}
TypeIOwn& operator=( TypeIOwn const& ) {return *this;}
TypeIOwn& operator=( TypeIDoNotOwn const& ) {return *this;}
};
но я получаю серию неоднозначных перегрузок:
main.cpp:31:4: error: use of overloaded operator '=' is ambiguous (with operand types 'TypeIDoNotOwn' and 'TypeIOwn') b = a; ~ ^ ~ main.cpp:9:17: note: candidate function TypeIDoNotOwn& operator=(TypeIDoNotOwn const&) {return *this;} ^ main.cpp:10:17: note: candidate function TypeIDoNotOwn& operator=(char const*) {return *this;}
и
/usr/include/c++/v1/utility:315:15: error: call to constructor of 'TypeIDoNotOwn' is ambiguous : first(_VSTD::forward<_U1>(__p.first)), ^ ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ main.cpp:40:51: note: in instantiation of function template specialization 'std::__1::pair<TypeIDoNotOwn, TypeIDoNotOwn>::pair<TypeIOwn, TypeIOwn>' requested here std::pair<TypeIDoNotOwn, TypeIDoNotOwn> f = std::make_pair( TypeIOwn(), TypeIOwn() ); ^ main.cpp:7:7: note: candidate constructor TypeIDoNotOwn(TypeIDoNotOwn const&) {} ^ main.cpp:8:7: note: candidate constructor TypeIDoNotOwn(char const*) {} ^
В моем "реальном" коде у меня есть другие операторы, такие как +=
и ==
, которые имеют схожие проблемы.
Объем реальной проблемы большой; миллионы строк кода, и я хочу поменять TypeIDoNotOwn для TypeIOwn на многих тысячах мест, но не на многих сотнях других. И в тысячах мест они взаимодействуют таким образом, что вызывает неоднозначность преобразования.
Я решил проблему функции, принимающей TypeIDoNotOwn&
в 100s пятен, где это происходит, обернув ее макросом, который создает временный объект, который создает TypeIDoNotOwn
из TypeIOwn
, возвращает ссылку на что тогда, когда временный объект уничтожается, копирует его обратно в TypeIOwn
. Я хочу избежать необходимости выполнять аналогичную развертку для обработки ==
, +=
, =
, копирования-построения и подобных ситуаций.
Если я попытаюсь удалить operator TypeIDoNotOwn
, чтобы устранить эту неоднозначность, в других случаях, когда требуется преобразование, не работает правильно (поскольку для получения от TypeIOwn
до TypeIDoNotOwn
требуется 2 пользовательских построения), который затем требует явного преобразования (на многих 100 или 1000 мест)
Если бы я мог сделать одно преобразование хуже, чем другое, это сработало бы. В противном случае я мог бы попытаться исправить случаи не operator=
и copy-construct, перегрузив бесплатный оператор TypeIDoNotOwn == TypeIOwn
с точным сопоставлением (и аналогичным для других случаев), но это не создает мне конструкцию, вызовы функций и назначение.