Маленькая глупенькая загадка-вотзефак. Не из рабочего кода, к счастью, но, как говорится, был близок к тому
Представьте себе, что у вас в коде есть функция f(x) и концепт fable, отвечающий на вопрос — можно ли подставить этот тип в f.
Но этот концепт работает... как-то странно!
Поэтому сделаем два одинаковых концепта — просто скопипастим их.
И даже напишем функцию, которая проверяет их оба.
Здравствуйте, Кодт, Вы писали:
К>До того, как всё сломалось, — всё работало: https://gcc.godbolt.org/z/dPPE6KrYe
К>Попробуйте догадаться, в чём дело. К>А если догадаетесь, то попробуйте написать минималистичный wish-you-happy-debug
Подглядел отгадку, т.к. с новыми стандартами ещё не знаком.
Если правильно понял мысль, где двое, там и трое соберутся:
#include <iostream>
struct A {}; // fablestruct B {}; // non-fablestruct C {}; // wtftemplate<class T, int I = __LINE__> concept fable1 = requires(const T & t) { f(t); I; };
#define FABLE(_class) fable1<_class, __LINE__>
template<class T, int I = __LINE__> void g() {
if constexpr (fable1<T,I>) {
std::cout << I << ": " << typeid(T).name() << " fable" << std::endl;
} else {
std::cout << I << ": " << typeid(T).name() << " non-fable" << std::endl;
}
}
void f(A) {} // fable
static_assert(!FABLE(C)); // LINE 20!void f(C) {} // fable ???
static_assert( FABLE(A));
static_assert(!FABLE(B));
static_assert( FABLE(C)); // LINE 26!int main() {
g<A>(); // fable
g<B>(); // non-fable
g<C, 20>(); // non-fable LINE 20
g<C, 26>(); // fable LINE 26
g<C>(); // fable
}
Здравствуйте, F3V, Вы писали:
F3V>Подглядел отгадку, т.к. с новыми стандартами ещё не знаком.
Этот фокус можно и без концептов провернуть. Проблема в точке инстанцирования шаблона (в данном случае — булевой константы со специальным синтаксисом и специальной семантикой).
auto f1(auto x) { return f(x); }
auto f2(auto x) { return f(x); }
char f(auto) { return 0; }
struct A {};
static_assert(sizeof(f1(A{})) == 1); // инстанцировали f1 здесь (где видна только char f)int f(A) { return 0; }
static_assert(sizeof(f1(A{})) == 1);
static_assert(sizeof(f2(A{})) == sizeof(int)); // инстанцировали f2 здесь
F3V>Если правильно понял мысль, где двое, там и трое соберутся:
по поводу __LINE__: оно там в паре мест не по делу. Но это не влияет.
F3V>template<class T, int I = __LINE__ /* = 7 */> concept fable1 = requires(const T & t) { f(t); I; };
F3V>template<class T, int I = __LINE__ /* = 10 */> void g() {
F3V>Но если убрать там I; то на gcc перестанет компилироваться, хотя студия и clang соберут без ошибок.
Гусь кеширует значения requires и проверяет, что они изменились.
If, at different points in the program, the satisfaction result is different for identical atomic constraints and template arguments, the program is ill-formed, no diagnostic required.
Здравствуйте, Кодт, Вы писали:
К>по поводу __LINE__: оно там в паре мест не по делу. Но это не влияет. К>
template<class T, int I = __LINE__ /* = 7 */> concept fable1 = requires(const T & t) { f(t); I; };
template<class T, int I = __LINE__ /* = 10 */> void g() {
Тут был эксперимент: захотелось в местах __LINE__ синтаксически управляемой лексической фазы разбора исходника во время инстанциирования синтаксического шаблона, чтобы обойтись без макроса FABLE и сохранить исходную лексику примера. (Может такую хотелку и реализуют в стандарте каком-нибудь когда-нибудь.)
В качестве воркэраунда, тут нашлась синтаксическая анонимная лямбда.
Здравствуйте, vdimas, Вы писали: V>А есть внятное объяснение, почему это работает? ))
При каждом обращении на значение fable делается заново переисчисление Entropy (в связи с механизмом его поставки через дефолтное значение), а там кложура, которая каждый раз новая/уникальная. Таким образом каждый раз получаем новую инстанцию с новой Entropy, ну и как побочный эффект, перевычисляется наша полезная нагрузка. Кстати, такое не обязательно делать на констрейнтах, можно и на SFINAE
например
#include <type_traits>
template <class T, auto e, class=void>
struct Fable : std::false_type {};
template <class T, auto e>
struct Fable<T, e, std::void_t<decltype(f(T{}))>> : std::true_type {};
template <class T, auto e = []{}>
constexpr bool fable = Fable<T, e>::value;
struct C{};
static_assert(!fable<C>);
void f(C);
static_assert(fable<C>);