Скажем, у меня есть класс шаблона, который создает несколько static_asserts:
template <class T>
class Foo
{
static_assert(!std::is_const<T>::value,"");
static_assert(!std::is_reference<T>::value,"");
static_assert(!std::is_pointer<T>::value,"");
//...<snip>...
}
Теперь предположим, что у меня есть другие классы шаблонов, которым нужно делать одни и те же утверждения.
Есть ли способ сделать блок static_assert многоразовым? "Функция static_assert", если хотите.





Вы можете просто объединить необходимые черты в одну с описательным именем:
template<typename T> using
is_fancy = ::std::integral_constant
<
bool
, (not std::is_const<T>::value)
and
(not std::is_reference<T>::value)
and
(not std::is_pointer<T>::value)
>;
и использовать его позже:
static_assert(std::is_fancy<T>::value,"");
Полностью вопрос предпочтений, но я бы предпочел унаследовать bool_constant.
@SergeyA Хороший вопрос, хотя обычно я просто объявляю псевдоним, потому что наследование, похоже, создает большую нагрузку на компилятор.
Интересное наблюдение о стоимости наследования и псевдонима во время компиляции. Никогда не проверял, как вы это измеряли?
@SergeyA создание экземпляра нового типа обычно сложнее, чем разрешение псевдонима.
@GuillaumeRacicot конечно, но мне интересно, поддается ли эффект количественному измерению.
@SergeyA «мера», вероятно, была бы смелым термином, но в течение некоторого времени я боролся с ошибками компиляции, вызванными тем, что компилятору (gcc) не хватило ОЗУ на некоторой базе кода с относительно большим количеством шаблонов. И ограничение порождения новых типов шаблонов (особенно рекурсивным образом) оказалось наиболее эффективным способом противодействия им.
Одна вещь, которую вы можете сделать, это создать новый трейт, который будет conjunction трейтов, которые вы хотите проверить. Поскольку вы хотите отрицать все те черты, которые буквально означают
template<typename T>
using my_trait = std::conjunction<std::negation<std::is_const<T>>,
std::negation<std::is_reference<T>>,
std::negation<std::is_pointer<T>>>;
static_assert(my_trait<int>::value, "");
но необходимость использовать std::negation для каждой черты может быть мучительной. Вы можете избавиться от этого, используя std::disjunction, чтобы получить «или» всех признаков, а затем просто отменить значение в статическом утверждении, как вы это делаете, что дает вам
template<typename T>
using my_trait = std::disjunction<std::is_const<T>,
std::is_reference<T>,
std::is_pointer<T>>;
static_assert(!my_trait<int>::value, "");
@JeJo Это еще один правильный вариант. Есть довольно много способов освежевать эту кошку. Я просто не хотел вводить какие-либо фактические переменные.
@SergeyA Вау. Я все испортил. Спасибо за место. Я обновил ответ.
Вы можете определить constexpr bool, который выполняет оценку во время компиляции:
template<typename T>
inline constexpr bool is_okay_type = !std::is_const<T>::value &&
!std::is_reference<T>::value &&
!std::is_pointer<T>::value;
Тогда либо:
используйте его прямо static_assert<>, как в вашем примере:
template<typename T> class MyClass
{
static_assert(is_okay_type<T>, "message");
public:
//...code...
};
или можно сделать условное создание экземпляра класса шаблона, в зависимости от аргумента шаблона.
template<typename Type, typename Enable = void> class Class1;
template<typename Type>
class Class1<Type, std::enable_if_t<is_okay_type<Type>> >
{
//...code...
};
Я видел несколько хороших ответов, используя союз. К сожалению, их действительно трудно отлаживать. Однажды мне пришлось отлаживать проблему с моим классом, в котором говорилось: требования выполнены. Это был слишком длинный список, чтобы понять. Наконец, я скопировал все базовые проверки одну за другой.
Когда это возможно, я предпочитаю разделять их:
template<typename T>
struct CustomCheck {
static_assert(check<T>);
// ...
};
В вашем классе вам нужно только создать его экземпляр, чтобы получить проверку и подробную ошибку:
constexpr static CustomCheck<T> check{};
Если я правильно понял, что вы подразумеваете под блоком
static_assert, похоже, вы можете поместить эти утверждения в класс шаблона и наследоватьFooот него. Дайте мне знать, если вам нужен код.