Open ssoft-hub opened 1 year ago
Нововведения в стандарт c++23 p0847r6 позволяют написать обертку в виде
#include <memory>
template < typename Self, typename Type >
using like_t = decltype( ::std::forward_like< Self >( ::std::declval< Type >() ) );
template < typename T >
struct Proxy
{
T m_v;
template < typename Self >
operator like_t< Self, T > ( this Self && self )
{
return ::std::forward_like< Self >( self.Proxy::m_v );
}
};
struct Data {};
using ProxyData = Proxy< Data >;
void bar ( Data && other ) {}
void bar ( Data const && other ) {}
void bar ( Data & other ) {}
void bar ( Data const & other ) {}
ProxyData foo () { return {}; }
ProxyData const cfoo () { return {}; }
int main ()
{
// rvalue / mutable
{
Data data = foo();
data = foo();
bar( foo() );
}
// rvalue / const
{
Data data = cfoo();
data = cfoo();
bar( cfoo() );
}
// lvalue / mutable
{
ProxyData proxy;
Data data = proxy;
data = proxy;
bar( proxy );
}
// lvalue / const
{
ProxyData const proxy;
Data data = proxy;
data = proxy;
bar( proxy );
}
return 0;
}
Такая реализация без проблем собирается и правильно работает. На мой взгляд это ещё один повод, чтобы явная реализация операторов преобразования работала подобным образом.
PS: Так же хорошо бы в стандарт добавить тип ::std::like_t наравне с добавленным уже ::std::forward_like.
Ссылка на исходный код godbolt
Скоро C++ будет как Perl только C++. И глядя на код можно будет сразу точно сказать, что вообще не понятно что он делает и почему делает именно так, то что он делает и где прячится UB из за которого иногда происходит не совсем то что было задумано изначально.
В языке существует особенность, которая проявляется при реализации паттерна Adapter (Proxy, Wrapper).
Простейшая реализация Proxy, который агрегирует значение и при преобразовании к типу вложенного значения сохраняет свойства rvalue/lvalue и контантность, выглядит следующим образом:
Случай для спецификатора volitile добавляет еще 4 варианта методов, но для упрощения здесь не рассматриваются.
Явное преобразование типа Proxy к типу вложенного значения с помощью метода get() позволяет бесшовно использовать экземпляры Proxy. Следующий код позволяет в этом убедиться:
Не всегда удобно использовать метод get(), особенно при множественном вложении значения в разные Proxy. Хотелось бы использовать экземпляр Proxy в выражениях наравне с экземплярами вложенного типа "прозрачно", без явного приведения с помощью метода get().
Если попытаться заменить явный метод get() на пользовательский оператор преобразования типа, то это приведет к ошибкам компиляции для временного экземпляра Proxy (компилятор gcc7 данный код компилирует без ошибок).
Такая ситуация связана с тем, что результат функции foo() одинаково хорошо стандартно преобразуется в ссылку rvalue и константную ссылку lvalue на временный экземпляр Proxy, который в свою очередь может быть одинаково пользовательски преобразован в ссылку rvalue и константную ссылку lvalue на внутреннее значение. Цепочки преобразований равнозначные - компилятор не может выбрать одну из них.
Если стандартизировать преобразование в ссылку rvalue предпочтительнее преобразования в константную ссылку lvalue, то данную ситуацию можно было бы разрешить однозначно верно.
Такое изменение не привнесет нежелательных побочных эффектов в существующую кодовую базу, так как только уточняет правила связывания временных объектов с сылками (работает в gcc7) и позволит реализовать пользовательские операторы преобразования типов с сохранением свойств rvalue/lvalue и const/volatile.
Ссылка на исходный код godbolt