Расслабить требования к передаче по ссылке временных объектов (как в MSVC)

Даниил Милютин
Даниил Милютин

Пример.

struct ReferenceType
{
    ReferenceType(int* p, int s): ptr(p), size(s) {}
    ~ReferenceType() {}
    // contract:
    //   some non-allocating functionality
    //   data can be accessed and modified
    //     but not allocated/deallocated
    int at(int i) const { return ptr[i]; }
    int& at(int i) { return ptr[i]; }
protected:
    int* ptr = nullptr;
    int  size = 0;
};

struct ValueType: ReferenceType
{   
    // just memory management

    ValueType(int N): ReferenceType(new int[N], N) {}
    ~ValueType() { delete[] ptr; }
};

void do_smth(ReferenceType& w) { w.at(0) = 10; }
void do_smth_rvr(ReferenceType&& w) { w.at(0) = 10; }

void do_other(int& i) {}

void do_other_rvr(int&& i) { do_other(i); }

int main() {
    ValueType s(3);
    do_smth(s); // ok // converted to ReferenceType&

    int data[3];
    //do_smth(ReferenceType(+data, 3));// error // ok - only MSVC
    //do_smth(ValueType(3));     // error // ok - only MSVC

    

    do_other(data[0]); // ok
    //do_other(10); // error. And that's fine for me!
                     // MSVC said: 'void do_other(int &)': 
                     //             cannot convert argument 1 from 'int' to 'int &')

    do_smth_rvr(ReferenceType(+data, 3)); // that's eaten by MSVC and gcc
    do_other_rvr(10);// that's eaten by MSVC and gcc
                     // so temporary is implicitly created there

    return data[0];
}

См. также:
Пример (by Jason Turner):
https://www.youtube.com/watch?v=SnTV5BU9x6k

Пример горького опыта близкий к моему:
https://forum.kde.org/viewtopic.php?f=74&t=88577

Предлагается позволить указанному расширению MSVC войти в новый стандарт.

З.Ы.
Какие обходы для текущей стуации?
1) Перегружать функцию принающую rv-reference. Не хочется по причине разрастания boilerplate.
К тому же если, аргументов больше одного (N), то придётся писать 2^N вариантов. Совсем не хочется.

2) Также можно было бы передавать ReferenceType по значению. Но это увеличит число копирований.
А ReferenceType всё таки может весить побольше.

3) Самый неправильный (и быстрый по написанию, и возможно по работе) workaround - это нагло врать компилятору.
Прописывать аргумент как постоянную ссылку, а внутри приводить с const_cast к мутабельному состоянию.
В примере выше можно и без оного - в лоб, ибо const int* позволит мутировать данные, на которые указывает.
Однако если ReferenceType устроен сложнее (например, содержит данные защищённо и к данным имеет доступ только через non-const методы), то придётся делать const_cast.

-2
рейтинг
5 комментариев
yndx-antoshkka

У этого расширения очень большие проблемы:

void increment(long& foo) { foo += 1; }
int main() {
    int x = 123;
    increment(x);
    assert(x == 124); // Oops!
}

Сами инженеры из MSVC рекомендуют не пользоваться этой особенностью.

yndx-antoshkka
Даниил Милютин

yndx-antoshkka, 
Ваш контр-пример не скомпилируется MSVC 

https://godbolt.org/z/TfNi7Q

void increment(long& foo) { foo += 1; }

struct X 
{
     X(long* x): i(x) {}
     long* i = nullptr;
};

void increment(X& foo) { *foo.i += 1; }

int main() {
    int x = 123;
    long ll = 123;
    //increment(x); // MSVC won't allow that
    // implicit type conversion in this case is forbidden!
    increment(ll); // fine
    increment(X(&ll)); // fine
    //assert(x == 124); // Oops!
    //assert(ll == 125); // Fine
    return ll;
}

Моё предложение касается случаев, когда при implicit конвертации не создаётся новых объектов на стеке.
В вашем примере (если б это работало по логике полного расслабления требований) создастся временный объект типа long. И я такое также ни в коей мере не поощряю.
То есть предложение звучит "как расслабить требования, но с ограничениями".

Ваше видение предложения изменилось после моих уточнений? :)

Даниил Милютин
Andrey Davydov

Даниил Милютин, MSVC ведят себя еще более странно -- важно не только то, создается ли временный объект явно или нет, но и является ли он примитивным типом: к примеру, increment((long)x) тоже не скомпилируется.

Andrey Davydov
Даниил Милютин

Andrey Davydov, Такое поведение компилятора MSVC меня лично тоже устривает.
Собственно свои идеи и уочнения я изложил.

Жду от коллег по цеху выраженных мыслей по этому поводу.
Если есть ещё логические доводы против, то мне интересно их воспринять.

Даниил Милютин
webreh

Преобразование prvalue of T в T& является предупреждением 4238: nonstandard extension used: class rvalue used as lvalue. Оно было нужно в С++03 и бесполезно с появлением форм auto&&.

webreh
Другие идеи
Группа создана, чтобы собирать предложения к стандарту C++, организовывать их внутренние обсуждения, помогать готовить их для отправки в комитет и защищать на общих собраниях в рабочей группе по С++ Международной организации по стандартизации (ISO).
Все предложения