Нижеприведенная программа была максимально сокращена, чтобы показать проблему, возникающую при использовании компилятора Visual Studio C++.
f
— это некоторая функция-алгоритм, принимающая объект входного предиката P p
, который имеет определяемый пользователем конструктор копирования, запоминающий указатель на исходный объект. В этом конструкторе проверяется, что объекты источника и копии действительно различны if (s == this) throw 0;
, но в operator ()
та же проверка возвращает противоположный результат:
struct P {
const P * s = nullptr;
constexpr P() {}
constexpr P(const P & p) : s(&p) {
if (s == this) throw 0; // never happens
}
constexpr bool operator()() const {
return s != this; // shall be always true?
}
};
constexpr bool f(P p) {
return p.s ? p() : f(p);
}
int main() {
static_assert( f(P{}) ); // fails in MSVC, where static_assert( f(P{}) == false );
}
Онлайн-демо: https://gcc.godbolt.org/z/nqYoshExj
Как можно объяснить, что одна и та же проверка проходит в конструкторе объекта, но затем завершается неудачно в его методе?
Кстати, это самое маленькое эквивалентное преобразование, которое я нашел, которое заставило msvc принять утверждение gcc.godbolt.org/z/Wr1qjzKox.
Это определенно ошибка в msvc.
msvc (но не clang и gcc) имеет ту же проблему с этим утверждением:
constexpr bool g(int x, void* s = nullptr) {
if (s) return &x != s;
return g(x,&x);
}
static_assert(g(0));
Если исключить рекурсию (она может иметь глубину только 1 уровень), то msvc также принимает утверждение:
constexpr bool h(int x, void* s) {
return &x != s;
}
constexpr bool h2(int x) {
return h(x,&x);
}
static_assert(h2(0));
Тот же рефакторинг вашего кода имеет тот же эффект (https://gcc.godbolt.org/z/69b57TPd8).
@AviBerger — рекурсивный вызов
f
вызывает копирующий c'tor для своего параметра, которыйs(&p)
— не ноль