Влияет ли неопределенное поведение на static_assert? - PullRequest
10 голосов
/ 06 марта 2019

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

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?

Ответы [ 2 ]

2 голосов
/ 06 марта 2019

Поскольку вы знаете, что будете использовать 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); /*...*/; /*...*/)
{
    /*...*/
}
1 голос
/ 06 марта 2019

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

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