Влияет ли неопределенное поведение на static_assert?

Рассмотрим следующий код:

SomeType x=getX();
for(auto mask = 1u<<(CHAR_BIT*sizeof x - 1);/*...*/;/*...*/)
{
    static_assert(sizeof mask>=sizeof x, "Type of numeric parameter is too long");
    /*...*/
}

Здесь mask будет иметь тип unsigned. Предположим, что SomeType равно long long. Тогда инициализация mask будет иметь неопределенное поведение из-за слишком большого смещения. Но OTOH, есть static_assert, который проверяет, что неопределенное поведение не может произойти во время выполнения (потому что код не скомпилируется).

Но поскольку UB может привести к временным парадоксам и другим неожиданностям, я не уверен, что static_assert действительно сработает в этом случае. Есть ли основания быть в этом уверенным? Или этот код следует переделать, чтобы static_assert отображалось до инициализации mask?


person Ruslan    schedule 06.03.2019    source источник
comment
Вы можете использовать auto mask = 1u; static_assert(...); mask <<= HAR_BIT*sizeof x-1;, чтобы полностью избежать этой проблемы.   -  person R Sahu    schedule 06.03.2019
comment
УБ сложный. Просто переместите утверждение над вычислением, чтобы убедиться, что оно имеет правильные значения, такие как SomeType x=getX(); static_assert(sizeof(x) < whatever_value_you_need, "Type of numeric parameter is too long"); ....   -  person NathanOliver    schedule 06.03.2019
comment
@RSahu Я отредактировал код примера, чтобы он был менее упрощен и ближе к моему реальному варианту использования.   -  person Ruslan    schedule 06.03.2019
comment
@Ruslan, обновленный код дает веские основания для перемещения static_assert сразу после SomeType x = ...;. Какой смысл делать это в цикле?   -  person R Sahu    schedule 06.03.2019
comment
@RSahu, чтобы не повторять тип маски. т.е. если мне нужно, например. расширить тип маски, я бы просто заменил 1u на 1ull в одном месте. Но если я заранее явно объявлю unsigned mask, чтобы выполнить static_assert раньше, у меня возникнут две проблемы: 1) маска сохраняется после цикла (для избежания этого требуется дополнительная область видимости), 2) изменение типа в выражении также требует дополнительного изменения типа mask .   -  person Ruslan    schedule 06.03.2019
comment
Можем ли мы увидеть это с большим количеством скобок? Я немного расстроен, увидев сдвиг влево, умножение, размер и вычитание без каких-либо из них.   -  person Tim Randall    schedule 06.03.2019
comment
@Tim: я думаю, что это (1u << (CHAR_BIT*(sizeof x))) - 1   -  person Ben Voigt    schedule 07.03.2019
comment
@BenVoigt, вы ошиблись: сложение/вычитание имеет более высокий приоритет, чем сдвиги. Но хорошо, это просто доказывает точку зрения Тима. Я добавлю несколько скобок.   -  person Ruslan    schedule 07.03.2019
comment
К сожалению, здесь нет ответа на вопрос   -  person James    schedule 03.02.2020


Ответы (2)


Поскольку вы знаете, что собираетесь использовать unsigned в качестве типа для mask, нет необходимости полагаться на mask для выполнения static_assert. Сделайте это сразу после начала цикла.

SomeType x = getX();
static_assert(sizeof 1u >= sizeof x, "Type of numeric parameter is too long");

for(auto mask = 1u << CHAR_BIT*sizeof x-1; /*...*/; /*...*/)
{
    /*...*/
}

Более чистым вариантом было бы использование вспомогательной функции.

template <typename RetType, typename SomeType>
RetType make_mask(RetType in, SomeType const& x)
{
   static_assert(sizeof in >= sizeof SomeType, "Type of numeric parameter is too long");
   return (in << (CHAR_BIT*sizeof SomeType)-1);
}

и использовать

for(auto mask = make_mask(1u, x); /*...*/; /*...*/)
{
    /*...*/
}
person R Sahu    schedule 06.03.2019
comment
Недостатком этого подхода является то, что вы повторяете 1u и добавляете стоимость обслуживания (на это намекает Руслан в комментариях, но не в вопросе) - person Tim Randall; 06.03.2019
comment
@TimRandall, верное замечание. Я обновил ответ, чтобы решить эту проблему. - person R Sahu; 06.03.2019

Если SomeType является интегральным типом и вы используете С++ 11 или новее, вы можете полностью исключить утверждение, используя:

auto one = std::make_unsigned<SomeType>::type(1);
for(auto mask = one << CHAR_BIT*sizeof x-1; /*...*/; /*...*/)
{
    /*...*/
}
person Kit.    schedule 06.03.2019