Здравствуйте, Went, Вы писали:
W>Ничего не понял. У меня задача не получить lvalue из чего угодно, а сгенерировать временный lvalue-объект, уникальный для каждого вызова. То есть, чтобы было что-то наподобие W>
W>foo(discard<int>())
W>
W>Но в идеале без <int> (хотя это вряд ли достижимо без граблей) и с минимальным оверхедом по синтаксису и производительности.
Самое простое, что сразу приходит в голову:
template<typename T>
struct discarding_arg
{
T _tmp;
public:
operator T&() &&
{
return _tmp;
}
};
void f(int & i) {
i = 0;
}
int main()
{
f(discarding_arg<int>{});
}
template<typename T>
T& discarding_arg (T&& _tmp = {})
{
return std::forward<T&>(_tmp); // До C++23 можно просто "return _tmp;"
}
void f(int & i) {
i = 0;
}
int main()
{
// Вариантов использования появляется чуть больше:
f(discarding_arg<int>());
f(discarding_arg(42));
g(discarding_arg(std::vector{1, 2, 3}));
}
Только если присмотреться, окажется, что discarding_arg — это переименованный lvalue
Здравствуйте, Went, Вы писали:
W>Но в идеале без <int> (хотя это вряд ли достижимо без граблей) и с минимальным оверхедом по синтаксису и производительности.
Более продвинутый вариант без необходимости явно описывать тип:
Здравствуйте. В C# есть "discarding _", который по сути создаёт временный объект и возвращает для него ref, чтобы принимающая функция, кортеж или выражение могли благополучно в него написать и забыть. Как получить подобное поведение в С++? То есть чтобы некий довольно краткий синтаксис мог быть передан в любую функцию вместо параметра, принимающего не-константную ссылку? Например?
// Что нужно написать вместо ..., чтобы работало нижнее?void foo(int&);
foo(...);
... = 1;
[x, ...] = get_some_struct();
Очевидно, что напрашивается функция, которая возвращает статическую переменную, объявленную внутри себя. Но в этом случае у нас получится один член на все вызовы, что чревато неожиданным поведением. Также не хотелось бы явно указывать тип аргумента, то есть discard() без <int> в конце, но это не обязательно. Также не хочется ничего на куче создавать без необходимости. Ну и ограничение на стандарт — С++14. Какие могут быть варианты?
Здравствуйте, Went, Вы писали:
W>Здравствуйте. В C# есть "discarding _", который по сути создаёт временный объект и возвращает для него ref, чтобы принимающая функция, кортеж или выражение могли благополучно в него написать и забыть. Как получить подобное поведение в С++? То есть чтобы некий довольно краткий синтаксис мог быть передан в любую функцию вместо параметра, принимающего не-константную ссылку? Например? W>
W>// Что нужно написать вместо ..., чтобы работало нижнее?
W>void foo(int&);
W>foo(...);
W>... = 1;
W>[x, ...] = get_some_struct();
W>
W>Очевидно, что напрашивается функция, которая возвращает статическую переменную, объявленную внутри себя. Но в этом случае у нас получится один член на все вызовы, что чревато неожиданным поведением. Также не хотелось бы явно указывать тип аргумента, то есть discard() без <int> в конце, но это не обязательно. Также не хочется ничего на куче создавать без необходимости.
W>Ну и ограничение на стандарт — С++14. Какие могут быть варианты?
Ну как раз вот это и хреново, поскольку самым нормальным решением был бы переход от конкретных типов к концептам.
Ну а с озвученными ограничениями:
template <typename T>
T& lvalue(T&& t) {
return t; // На C++23 не проканает - нужно будет использовать forward<T&>(t)
}
foo(lvalue(42));
--
Справедливость выше закона. А человечность выше справедливости.
Здравствуйте, rg45, Вы писали:
R>Ну как раз вот это и хреново, поскольку самым нормальным решением был бы переход от конкретных типов к концептам. R>Ну а с озвученными ограничениями:
R>
R>template <typename T>
R>T& lvalue(T&& t) {
R> return t; // На C++23 не проканает - нужно будет использовать forward<T&>(t)
R>}
R>foo(lvalue(42));
R>
Ничего не понял. У меня задача не получить lvalue из чего угодно, а сгенерировать временный lvalue-объект, уникальный для каждого вызова. То есть, чтобы было что-то наподобие
foo(discard<int>())
Но в идеале без <int> (хотя это вряд ли достижимо без граблей) и с минимальным оверхедом по синтаксису и производительности.
Аналогично, не понимаю при чем тут это. У меня задача — автоматизированная трансляция C# кода в С++. Хочу найти оптимальную замену "discarding _". Другими словами, во что транслировать такое:
void foo(out int x)
{
x = 1;
}
foo(out var _);
Получаем такое:
void foo(int& x)
{
x = 1;
}
foo(/*что сюда написать? чтобы универсально и портабельно?*/);
Здравствуйте, Went, Вы писали:
W>Ничего не понял. У меня задача не получить lvalue из чего угодно, а сгенерировать временный lvalue-объект, уникальный для каждого вызова. То есть, чтобы было что-то наподобие W>
W>foo(discard<int>())
W>
W>Но в идеале без <int> (хотя это вряд ли достижимо без граблей) и с минимальным оверхедом по синтаксису и производительности.
Да я понял, что тебе нужно. Просто идеального дискардинга, как в C# не получается. Например, приведенный тобой вариант будет работать только для типов с дефолтным конструктором (в дополнение к необходимости явной спецификации типа). Вариант с lvalue просто позволяет сконструировать налету временный объект любого конструируемого типа и передать его в функцию по lvalue ссылке. Синтаксического сахара, конечно, меньше, чем в C#. Зато получается настоящий временный объект (не на куче и не в статике), который умрет в конце полного выражения или инициализатора. Это должно хорошо поддаваться оптимизации, поскольку компилятору прекрасно видно, что это объект "навыброс". Также охватываются типы, для которых отсутсвует возможность конструирования по дефолту (или для вызываемой функции не пофигу, с какими параметрами сконструирован объект).
P.S. А вообще аутпут параметрами лучше не злоупотреблять. С ними обязательно какой-нибудь геморрой — не такой, так эдакий.
--
Справедливость выше закона. А человечность выше справедливости.
Здравствуйте, A.J., Вы писали:
AJ>Здравствуйте, Went, Вы писали:
W>>
W>>[x, ...] = get_some_struct();
W>>
AJ>Для случая с распаковкой get_some_struct можно использовать std::ignore
Да, но по ряду причин нужно универсальное решение, которое в частности должно передаваться в обычные функции.
AJ>>Для случая с распаковкой get_some_struct можно использовать std::ignore W>Да, но по ряду причин нужно универсальное решение, которое в частности должно передаваться в обычные функции.
А вот со structured bindings как раз всё просто, по-моему:
auto&& [x, _] = get_some_struct();
В более сложном случае:
auto&& [x, _0, _1, _2] = get_some_struct();
--
Справедливость выше закона. А человечность выше справедливости.
Здравствуйте, Went, Вы писали:
W>Аналогично, не понимаю при чем тут это. У меня задача — автоматизированная трансляция C# кода в С++. Хочу найти оптимальную замену "discarding _". Другими словами, во что транслировать такое:
void foo(out int x) { x = 1; }
..
foo(out var _);
foo(/*что сюда написать? чтобы универсально и портабельно?*/);
если есть foo(out var _); то добавить определение void foo(void);
Да, вот этот вариант я как раз и сделал в первую очередь. Но столкнулся с проблемой шаблонов — в таком случае выводится не T, а discarding_arg<T>, что не допустимо. Поэтому решил, что неявное приведение к T& лучше заменить явным вызовом:
foo(discard<int>().ref());
Многословно, но пока что ничего лучше в голову не пришло.
Здравствуйте, so5team, Вы писали: S>Более продвинутый вариант без необходимости явно описывать тип:
Да, но в этом случае у нас один экземпляр на все вызовы одного типа, если я правильно понял.
Здравствуйте, Went, Вы писали:
S>>Более продвинутый вариант без необходимости явно описывать тип: W>Да, но в этом случае у нас один экземпляр на все вызовы одного типа, если я правильно понял.
Да. Но если
a) ссылки на это значение нигде не сохраняются;
b) значения просто сразу же выбрасываются,
то это самый дешевый способ.
Из вашего описания не очень понятно насколько вам важно вот такое:
void f(int & x, int & y) {
x = 2;
...
y = 3;
...
x += y * x;
}
Т.е. когда промежуточные изменения внутри x для каждой из ссылок важны и должны быть сохранены для последующих вычислений.
Если это критически важно, то да, именно этот подход не сработает.
Здравствуйте, so5team, Вы писали: S>Если это критически важно, то да, именно этот подход не сработает.
Я разрабатываю общее решение, и не могу заранее знать, как оно будет использоваться Если в шарпе это разные экземпляры, то и у меня должно быть так )
Здравствуйте, Went, Вы писали:
W>Я разрабатываю общее решение, и не могу заранее знать, как оно будет использоваться Если в шарпе это разные экземпляры, то и у меня должно быть так )
Ну вот, и тебя дотнет покусал. Редеют наши ряды.
--
Справедливость выше закона. А человечность выше справедливости.