В шаблонном программировании static_assert
помогает программистам проверять ограничения на аргументы шаблона и генерировать человек читаемый сообщения об ошибках при нарушении ограничений.
Рассмотрим этот код,
template<typename T>
void f(T)
{
static_assert(T(), "first requirement failed to meet.");
static_assert(T::value, "second requirement failed to meet.");
T t = 10; //even this may generate error!
}
Моя мысль: если первый static_assert
не получается, значит немного требование к T
не встречается, следовательно, компиляция должна прекратиться, генерируя только первый сообщение об ошибке — потому что бессмысленно продолжать компиляцию, просто чтобы генерировать все больше и больше сообщений об ошибках, большинство из которых довольно часто указать на не замужем нарушение ограничений. Сотни сообщений об ошибках, вместо одного, выглядят очень страшно на экране — я бы даже сказал, это бросает вызов самому цель из static_assert
в некоторой степени.
Например, если я вызову шаблон функции выше, как:
f(std::false_type{});
GCC 4.8 генерирует следующее:
main.cpp: In instantiation of 'void f(T) [with T = std::integral_constant<bool, false>]':
main.cpp:16:24: required from here
main.cpp:7:5: error: static assertion failed: first requirement failed to meet.
static_assert(T(), "first requirement failed to meet.");
^
main.cpp:9:5: error: static assertion failed: second requirement failed to meet.
static_assert(T::value, "second requirement failed to meet.");
^
main.cpp:11:11: error: conversion from 'int' to non-scalar type 'std::integral_constant<bool, false>' requested
T t = 10; //even this may generate error!
Как вы видете (онлайн), это слишком много ошибок. Если первый static_assert
сбои, очень вероятно, что остальная часть кода будет также потерпеть неудачу если компиляция продолжается, зачем продолжать компиляцию? Я уверен, что в программировании шаблонов многие программисты не хотят таких каскадных сообщений об ошибках!
Я пытался решить эту проблему путем расщепление функция на несколько функций, в каждой проверяется только одно ограничение, как:
template<typename T>
void f_impl(T); //forward declaration
template<typename T>
void f(T)
{
static_assert(T(), "first requirement failed to meet.");
f_impl(T());
}
template<typename T>
void f_impl(T)
{
static_assert(T::value, "second requirement failed to meet.");
T t = 10;
}
f(std::false_type{}); //call
Теперь это создает это:
main.cpp: In instantiation of 'void f(T) [with T = std::integral_constant<bool, false>]':
main.cpp:24:24: required from here
main.cpp:10:5: error: static assertion failed: first requirement failed to meet.
static_assert(T(), "first requirement failed to meet.");
^
Это большое улучшение — только одно сообщение об ошибке намного легче читать и понимать (см. онлайн).
Мой вопрос
static_assert
? Я согласен с Дэвидом Родригесом — dribeas и в защиту авторов компиляторов рассмотрим этот пример:
#include <type_traits>
class A {};
// I want the nice error message below in several functions.
// Instead of repeating myself, let's put it in a function.
template <typename U>
void check() {
static_assert(std::is_convertible<U*, const volatile A*>::value,
"U doesn't derive publicly from A ""(did you forget to include it's header file?)");
}
template <typename U>
void f(U* u) {
// check legality (with a nice error message)
check<U>();
// before trying a failing initialization:
A* p = u;
}
class B; // I forget to include "B.h"
int main() {
B* b = nullptr;
f(b);
}
Когда экземпляр f<B>
запускает компилятор (или автор компилятора) может подумать: «Хм … Мне нужно создать экземпляр check<U>
и люди всегда жалуются, что компиляция шаблонов идет слишком медленно. Так что я продолжу, и, возможно, что-то не так внизу, и мне не нужно создавать экземпляры check
«.
Я считаю, что рассуждения выше имеют смысл. (Обратите внимание, что я не автор компилятора, так что я просто размышляю здесь).
И GCC 4.8, и VS2010 продолжают компилировать f<B>
, откладывая создание check<B>
Для последующего. Затем они находят неудачную инициализацию и предоставляют свои собственные сообщения об ошибках. VS2010 немедленно останавливается, и я не получаю свое хорошее сообщение об ошибке! GCC продолжает идти и выдает сообщение, которое я хотел (но только после своего).
Метапрограммирование сложно для программистов и для компиляторов. static_assert
помогает много но это не панацея.
Есть несколько целей, которые должны быть сбалансированы здесь. В частности, более простые сообщения об ошибках меньшего размера могут быть получены путем остановки первой ошибки, что хорошо. В то же время остановка на первой ошибке не дает вам информации о любых других проблемах, которые вы, возможно, захотите решить, прежде чем пытаться выполнить другую потенциально дорогостоящую компиляцию. Например, в вашем первом примере я лично предпочитаю все static_assert
ы должны быть проверены сразу. Прочитайте сообщение об ошибке как:
Вы не выполнили следующие требования:
value
типЯ предпочел бы, чтобы обе ошибки были обнаружены на первом проходе, чем исправить одну, и мне потребуется несколько минут, чтобы система сборки отключилась на следующем.
Предполагается, что компилятор может восстановиться после ошибки и продолжить синтаксический анализ, хотя грамматика зависит от контекста, и это не всегда так, поэтому часть негативной стороны проблемы заключается в том, что вы можете доверять первой ошибке, но Следующие ошибки могут быть следствием первой ошибки, и для того, чтобы понять, что есть что, нужен опыт.
Все это — качество реализации (следовательно, зависит от компилятора), и многие реализации позволяют вам определить, когда остановиться, так что это зависит от пользователя и флагов, которые передаются компилятору. Компиляторы получают лучшие отчеты об ошибках и исправляются после них, так что вы можете ожидать улучшения здесь. Для дальнейшего улучшения> C ++ 14 (C ++ 17? Позже?) Добавит концепции, предназначенные для улучшения сообщений об ошибках.
Подводя итог: