Почему удаление параметра по умолчанию нарушает этот счетчик constexpr?
рассмотрим следующий код, реализующий счетчик времени компиляции.
#include <iostream>
template<int>
struct Flag { friend constexpr int flag(Flag); };
template<int N>
struct Writer
{
friend constexpr int flag(Flag<N>) { return 0; }
};
template<int N>
constexpr int reader(float, Flag<N>) { return N; }
template<int N, int = flag(Flag<N>{})>
constexpr int reader(int, Flag<N>, int value = reader(0, Flag<N + 1>{}))
{
return value;
}
template<int N = reader(0, Flag<0>{}), int = sizeof(Writer<N>) >
constexpr int next() { return N; }
int main() {
constexpr int a = next();
constexpr int b = next();
constexpr int c = next();
constexpr int d = next();
std::cout << a << b << c << d << 'n'; // 0123
}
второй reader
перегрузка, если я помещаю параметр по умолчанию в тело функции, например:
template<int N, int = flag(Flag<N>{})>
constexpr int reader(int, Flag<N>)
{
return reader(0, Flag<N + 1>{});
}
тогда выход станет:
0111
Почему это происходит? Почему вторая версия больше не работает?
если это имеет значение, я использую Visual Studio 2015.2.
2 ответов
без value
передается в качестве параметра, ничто не мешает компилятору кэширование вызов reader(0, Flag<1>)
.
в обоих случаях первый next()
вызов будет работать, как ожидалось, так как это немедленно приведет к SFINAEing к reader(float, Flag<0>)
.
второй next()
оценить reader<0,0>(int, ...)
, от которой зависит reader<1>(float, ...)
который может быть кэширован, если он не зависит от
актуальность value
заключается в том, что она участвует в разрешении перегрузки. В соответствии с правилами SFINAE ошибки создания экземпляра шаблона молча исключить кандидатов из разрешения перегрузки. Но он создает экземпляр Flag<N+1>
, что заставляет разрешение перегрузки стать жизнеспособным в следующий раз (!). Таким образом, вы фактически подсчитываете успешные экземпляры.
почему ваша версия ведет себя по-другому? Вы все еще ссылаетесь Flag<N+1>
, но и в реализация функции. Это важно. С помощью шаблонов функций,декларация должно быть рассмотрено для SFINAE, но только выбранная перегрузка затем создается экземпляр. Ваше заявление просто template<int N, int = flag(Flag<N>{})> constexpr int reader(int, Flag<N>);
и не зависит от Flag<N+1>
.
как отмечено в комментариях, не рассчитывайте на этот счетчик;)