LINUX.ORG.RU

GCC vs Clang разный результат

 , ,


0

3

Помогите пожалуйста начавшему изучать C++. Никак не могу вдуплить в чем дело. Код:

#include <iostream>
#include <tuple>

class Channel
{
public:
  Channel() : x(0) {}
  Channel(float x) : x(x) {}
  float x;
};

template <typename... Args> class Bus
{
public:  
  Bus(Args&& ...args) : x{args...} {}  
  std::tuple<Args&...> x;
};

int main()
{
  Channel ch1;
  Bus<Channel, Channel> bus1((Channel(2.5)), (Channel(1.3)));
 
  std::cout << std::get<0>(bus1.x).x << std::endl;
  std::cout << std::get<1>(bus1.x).x << std::endl;
}

Проблема вот в чем - отличаются значения std::get<0>(bus1.x).x, std::get<1>(bus1.x).x

По моему разумению, должно получаться 2.5, 1.3. С clang все работает как я ожидаю, выдается 1.3. С gcc - только ели собрать с -O0 или -Og. Иначе выдается ноль.

Подскажите, в чем тут проблема? Видимо я не понимаю какой-то элементарной вещи, ну не должно же такого происходить?

Вот оно https://godbolt.org/z/a4h6Ms

★★★★★

Я не великий эксперт в C++, но зачем все эти ссылки?

Этот пример работает:

template <typename... Args> class Bus
{
public:  
  Bus(Args ...args) : x{args...} {}  
  std::tuple<Args...> x;
};
Octagon ()

О, утренняя головоломка.

Отличный вопрос.

intelfx ★★★★★ ()
Ответ на: комментарий от Octagon

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

intelfx ★★★★★ ()
Последнее исправление: intelfx (всего исправлений: 1)
Ответ на: комментарий от moonmadness

Стоп, лол, действительно.

std::tuple<Args&…> x;

ТС, ты вот тут объявил кортеж из ссылок. А тебе нужен кортеж из значений.

И потом становится можно сделать ещё вот так:

Bus(Args&& …args) : x{std::forward<Args>(args)…} {}

intelfx ★★★★★ ()
Последнее исправление: intelfx (всего исправлений: 2)
Ответ на: комментарий от Octagon

Это минимальный пример «не работы». Естественно, это нужно не в таком виде, а как часть реальной задачи.

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

Но я не уверен, что например при создании нового tuple<Channel, Channel, Channel> и копировании туда элементов из bus1 не будет копирования данных, а будет всегда перемещение.

curufinwe ★★★★★ ()

А ничего что у тебя в ссылки на временные обьекты в тупеле?

invy ★★★★★ ()
Ответ на: комментарий от intelfx

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

Мне думается, что если ТС хочет гибкости, то ему нужен не простой конструктор для своего Bus, а шаблонный конструктор с другим набором шаблонных параметров:

template <typename... Args> class Bus
{
public:
  template<typename... Params>
  Bus(Params && ...params) : x{std::forward<Params>(params)...} {}
  
  std::tuple<Args...> x;
};

Тогда у него в конструкторе && будет означать именно universal references. Если же написать как изначально было у ТС-а:

template <typename... Args> class Bus
{
public:  
  Bus(Args&& ...args) : x{args...} {}  

То в конструкторе && – это rvalue references.

eao197 ★★★★★ ()
Ответ на: комментарий от intelfx

А тебе нужен кортеж из значений.

Здесь меня смущает вот какой вопрос. Допустим я создал кортеж из значений. Потом мне надо создать новый кортеж, длиной на один элемент больше. Я могу как-то это сделать, исключив копирование значений из старого кортежа, то есть чтобы они перемещались в новый?

curufinwe ★★★★★ ()
Ответ на: комментарий от eao197

Да, спасибо, насчет конструктора я понял.

У меня основная проблема - как исключить копирование объектов Channel. То есть чтобы я создавал новые Bus, а Channel между ними перемещались а не копировались.

Поэтому я попробовал через tuple со ссылками, но тут засада со ссылками на временные объекты.

curufinwe ★★★★★ ()
Ответ на: комментарий от intelfx

Обычные float ты всё равно будешь копировать, потому что они по размеру меньше, чем указатели.

Что-то я не пойму, причем тут указатели.

curufinwe ★★★★★ ()
Ответ на: комментарий от curufinwe

Ссылка это указатель который не может быть nullptr

fsb4000 ★★★★★ ()
Ответ на: комментарий от curufinwe

Потому что скопировать флоат дешевле, чем создавать и копировать ссылку на него.

Или тебе нужны ссылки именно семантически?

intelfx ★★★★★ ()
Последнее исправление: intelfx (всего исправлений: 1)
Ответ на: комментарий от intelfx

Потому что скопировать флоат дешевле, чем создавать и копировать ссылку на него.

Мне нужно, чтобы в рантайме никаких ссылок не создавалось и не копировалось. Как и лишнего копирования данных не было. То есть этот Bus должен работать как обертка, живущая только в compile time.

curufinwe ★★★★★ ()
Ответ на: комментарий от curufinwe

Тогда твой код противоречит твоему намерению, потому что ты пишешь std::tuple<Args&...> — то есть тупль из ссылок на Channel’ы.

intelfx ★★★★★ ()
Ответ на: комментарий от curufinwe

Здесь меня смущает вот какой вопрос. Допустим я создал кортеж из значений. Потом мне надо создать новый кортеж, длиной на один элемент больше. Я могу как-то это сделать, исключив копирование значений из старого кортежа, то есть чтобы они перемещались в новый?

Вот тут ничего не скажу. Может быть, на шаблонной магии можно.

Но ещё раз повторю, перемещение — это абстракция. Если у тебя внутри Channel-а один флоат, он всё равно скопируется, без вариантов.

Перемещение будет иметь смысл, если у тебя внутри Channel-а будет, скажем, указатель на многомегабайтный массив. Тогда при копировании Channel-а тебе придётся выделять новый массив и копировать его, а при перемещении будет достаточно скопировать указатель и затереть его в предыдущем месте.

intelfx ★★★★★ ()
Ответ на: комментарий от intelfx

Наверное я не так выразился - надо чтобы в результате оптимизаций в рантайме не оставалось ссылок, указателей, копирования и прочего. Чтобы было статически. То есть чтобы в результате оптимизации, например, -O2, Bus исчезали.

curufinwe ★★★★★ ()
Ответ на: комментарий от intelfx

Но ещё раз повторю, перемещение — это абстракция. Если у тебя внутри Channel-а один флоат, он всё равно скопируется, без вариантов.

Не понимаю почему без вариантов.

Если у меня tuple ссылок и другой tuple ссылок, и я копирую ссылку из одного тапла в другой. Данные же не копируются, копируются ссылки.

Теперь - в компайл-тайме у меня неизвестные данные (значение float x из рантайма приходить будет), но полностью известные ссылки. То есть количество элементов во всех таплах, количество объектов Channel полностью известно в compile-time. Поэтому компилятор может просто выкинуть эти ссылки, что в принципе и видно в ассемблере - там ссылок не остается, если не собирать с -O0.

Не так?

curufinwe ★★★★★ ()
Ответ на: комментарий от intelfx

Почему это «все равно»? Если будет ссылка, то откуда взятся копированию. Я понимаю, что ссылки на этапе компиляции реализуются как указатели, но ведь потом они будут удалены и останутся чистые адреса переданной переменной.

rumgot ★★★★★ ()
Ответ на: комментарий от intelfx

@rumgot

Собственно вот примерчик того что я хотел между таплами

https://godbolt.org/z/bfYTT1

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

Если собрать с -O0, то ничего не выбрасывается и видно и добавление данных в таплы, и копирование между ними.

Интересно, что получается как я хотел даже если использовать таплы значений. То есть моя ошибка - с таплами ссылок, как сразу тут и пояснили, спасибо.

curufinwe ★★★★★ ()
Ответ на: комментарий от rumgot

Адреса переданной переменной — это и есть указатели.

Или мы тут как-то крупно друг друга не понимаем, или одно из двух.

intelfx ★★★★★ ()
Ответ на: комментарий от curufinwe

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

anonymous ()
Ответ на: комментарий от anonymous

ты же так тянешь кучу библиотечной реализации, в любом случае это будут накладные расходы

Это накладные расходы для компиляции, они меня не волнуют. И библиотечные реализации тут - стандартная библиотека плюсов, она и так есть всегда.

Для рантайма расходов быть не должно - по крайней мере пока в этих примерах их нет.

Бери тогда С без всей этой крестовой чепухи.

Для этого сейчас используется кодогенератор в C с классами. Я хочу выяснить, возможно ли его заменить на C++.

curufinwe ★★★★★ ()

длинно не читал, но наверное уже отвтетили

template <typename... Args> class Bus
{
public:  
  Bus(Args&& ...args) 
  : x(std::make_tuple(std::forward<Args>(args)...)) {}  
  std::tuple<Args...> x;
};
anonymous ()
Ответ на: комментарий от anonymous

Очевидно же, что нужно брать D. Знакомый синтаксис, доступна сишная библиотека и вообще бесшовная интеграция с сишечкой, можно мешать сишные и дишные функции и постепенно переходить. Плюс огромные возможности по метапрограммированию, мощнее и намного проще чем в крестах.

yetanother ★★ ()
Ответ на: комментарий от yetanother

D нимодно и нималадёжна! D нивзлител! тынипанимаешь! сам линус на раст переходит! раст взлетел!

anonymous ()
Ответ на: комментарий от eao197

Тогда у него в конструкторе && будет означать именно universal references. Если же написать как изначально было у ТС-а:

То в конструкторе && – это rvalue references.

Ты пишешь так, как будто universal (forwarding) reference это не rvalue reference.

anonymous ()
Ответ на: комментарий от anonymous

Нет конечно. Universal reference это может быть как lvalue так и rvalue reference в зависимости от того какой аргумент передали в конструктор.

fsb4000 ★★★★★ ()
Ответ на: комментарий от anonymous

Вроде бы нет. Там выше eao197 ответил, что для perfect forwarding конструктору нужно выдать свои собственные шаблонные аргументы.

intelfx ★★★★★ ()
Ответ на: 🤦‍♂️ от anonymous
#include <iostream>
#include <type_traits>
using namespace std;
template<typename T>
void test(T&& a)
{
  cout << is_rvalue_reference_v<decltype(a)> << '\n';
}
int main()
{
  int const a = 5;
  test(a);
  test(5);
}
fsb4000 ★★★★★ ()
Ответ на: комментарий от anonymous

D нимодно и нималадёжна! D нивзлител! тынипанимаешь! сам линус на раст переходит! раст взлетел!

С такими аргументами трудно поспорить ;)

yetanother ★★ ()
Ответ на: комментарий от anonymous

Читать оппонентов тоже нужно не жопой.

Речь о том, что universal reference — это rvalue reference на свободный шаблонный аргумент, который является каким-либо другим ссылочным типом. В результате при выведении типов ссылка на ссылку схлопывается либо в lvalue reference, либо в rvalue reference в зависимости от value category аргумента.

А у ТСа функция принимает строго rvalue reference на конечный тип, что не является универсальной ссылкой.

intelfx ★★★★★ ()
Последнее исправление: intelfx (всего исправлений: 1)
Ответ на: комментарий от intelfx

Речь о том, что universal reference — это rvalue reference

У меня про это речь изначально и шла. Только почему-то некоторым это утверждение не понравилось.

В результате при выведении типов ссылка на ссылку схлопывается либо в lvalue reference, либо в rvalue reference в зависимости от value category аргумента.

Так, и что?

А у ТСа функция принимает строго rvalue reference на конечный тип, что не является универсальной ссылкой.

А я с этим спорил?

anonymous ()
Ответ на: комментарий от curufinwe

ссылку нельзя скопировать, только проинициализировать. это не те же ссылки которые в джаве или других языках.

int x = 10;
int &y = x;

y - это теперь алиас для х, но обратиться к y как к ссылке нельзя, y = z не изменит значение ссылки, а изменит значение x на которое она ссылается. когда ты инициализируешь другой тупл ссылок, ты не копируешь ссылки, а инициализируешь ссылки в другом тупле какими-то значениями.

тебе надо включить -Wall -Wextra чтобы компилятор начал выдавать тебе разные предупреждения о временных объектах и поставь себе valgrind.

если тебе надо общий доступ к одному объекту, то надо сделать либо указатель, либо std::shared_ptr. ну или можешь посовокупляться со ссылками в том виде в котором ты их понимаешь, но всё равно ты свой объект где-то скопируешь или чего похуже, у тебя же даже предупреждения выключены. ждём на форуме с рассказами про то какой плохой с++.

anonymous ()
Ответ на: комментарий от anonymous

ссылку нельзя скопировать

Но при этом struct T { R& ref; }; является trivially copyable типом, а значит на T распространяется действие [basic.types]/3:

For any trivially copyable type T, if two pointers to T point to distinct T objects obj1 and obj2, where neither obj1 nor obj2 is a base-class subobject, if the underlying bytes ([intro.memory]) making up obj1 are copied into obj2, obj2 shall subsequently hold the same value as obj1.

Представляю, как из-за этого у Степанова или Шон Пэрэна горит.

anonymous ()
Ответ на: комментарий от anonymous

это базовые вещи которые надо знать, это знание появляется после первого s2 = s1 и после этого не пропадает.

но вообще мета-программирование - это плохая примета в любом случае. если в коде есть ... или enable_if, то это уже пдц, дальше можно не смотреть. пацанчик зашёл не в тот район, не та книжка по с++ попалась, и такие вещи пишут либо люди которые очень хорошо знают с++, либо люди которые с++ вообще не знают, а люди которые хорошо знают с++ таких вещей стараются избегать и в сумме получается то, что получается.

на месте опа я был оставил тот кодогенератор на «си с классами» в покое.

anonymous ()
Ответ на: комментарий от curufinwe

кстати посмотри ренжи из с++20, возможно это ответит на твои вопросы как передать несколько каналов без оверхеда в рантайме. на гитхабе есть range-v3 которая реализует ренжи на с++14/17 и далее.

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

anonymous ()
Вы не можете добавлять комментарии в эту тему. Тема перемещена в архив.