[edit: Вот мотивация: передача указателя переменной во внешнюю функцию может случайно нарушить некоторую оптимизацию для соседних переменных из-за возможности получить указатели на соседние переменные, вычисленные из исходного указателя внешней функцией. Ниже приведен исходный пост, в котором volatile
предназначен для имитации внешней функции, недоступной для текущего модуля компилятора, например. вызов виртуальной функции, функция библиотеки с закрытым исходным кодом и т. д.]
Мне было интересно, будет ли return t.a;
в следующем коде оптимизировано до return 0;
.
//revision 1
struct T
{
int a;
int b;
};
void f_(int * p)
{
*p = 1;
}
auto volatile f = f_;
int main()
{
T t;
t.a = 0;
t.b = 0;
for (int i = 0; i < 20; ++i)
{
f(&t.b);
}
return t.a;
}
Ну это не так. Достаточно справедливо, потому что код в функции f
может использовать offsetof
для получения указателя на t
, а затем изменить t.a
. Так что оптимизировать нагрузку t.a
небезопасно.
[править: если подумать, offsetof
здесь недостаточно. Нам нужен container_of
, который, кажется, невозможно реализовать в стандартном C++.]
Но offsetof
нельзя использовать для типов с нестандартной компоновкой. Итак, я попробовал следующий код:
//revision 2
#include <type_traits>
struct T
{
private:
char dummy = 0;
public:
int a;
int b;
};
static_assert(!std::is_standard_layout_v<T>);
void f_(int * p)
{
*p = 1;
}
auto volatile f = f_;
int main()
{
T t;
t.a = 0;
t.b = 0;
for (int i = 0; i < 20; ++i)
{
f(&t.b);
}
return t.a;
}
К сожалению, он все еще не работает.
Мои вопросы:
- безопасно ли оптимизировать загрузку
t.a
в приведенном выше случае (ревизия 2) - если нет, существует ли какая-то договоренность/предложение сделать это возможным? (например, создание
T
более специального типа или некоторый спецификатор атрибута для членаb
вT
)
P.S. Следующий код оптимизирован для return t.a;
, но полученный код для цикла немного неэффективен. И все же жонглирование временными переменными обременительно.
//revision 3
struct T
{
int a;
int b;
};
void f_(int * p)
{
*p = 1;
}
auto volatile f = f_;
int main()
{
T t;
t.a = 0;
t.b = 0;
for (int i = 0; i < 20; ++i)
{
int b = t.b;
f(&b);
t.b = b;
}
return t.a;
}
volatile
, поскольку ее правильное использование в C++ очень ограничено? Это похоже на проблему XY. - person Richard Critten   schedule 31.10.2020f
как volatile, вы сообщаете компилятору, чтоf
может быть изменен каким-то кодом, невидимым для компилятора. В первом случае компилятор должен учитывать возможностьf()
произвольного изменения в цикле и выполнения различных действий, например измененияt.a
на любой итерации цикла. Было бы вполне возможно изменитьf
, поэтомуf()
делает*(p-1) = 42
, поскольку это совершенно четко определенный способ измененияt.a
при передаче&t.b
, посколькуt.a
иt.b
находятся в одном и том же объекте (структуре данных). Аналогичное обсуждение ваших правок. - person Peter   schedule 31.10.2020