Давайте go через все шаблоны классов по порядку. Я собираюсь использовать более простую концепцию, поскольку bool
является единственным релевантным типом:
template <typename T>
struct Foo
{
Foo(T) requires same_as<T, bool>;
Foo(const auto&) requires (not same_as<T, bool>);
};
Foo(true);
Foo(3.14159);
При выполнении вывода аргументов шаблона класса мы получаем все конструкторы и превращаем их в функции - и затем выполните разрешение перегрузки, чтобы выяснить, к какому конкретному типу c мы пришли. Для Foo
это будет:
template <typename T> requires same_as<T, bool>
auto __f(T) -> Foo<T>;
template <typename T> requires (not same_as<T, bool>)
auto __f(const auto&) -> Foo<T>;
__f(true); // from Foo(true)
__f(3.14159); // from Foo(3.14159)
При первой перегрузке __f
, T
выводится из аргумента. Во второй перегрузке это не так - нет никакого способа определить, что такое T
... так что это в принципе не имеет значения, насколько идет процесс CTAD. В результате, __f(true)
в порядке (вы получаете обратно Foo<bool>
), но __f(3.14159)
плохо сформирован - первая перегрузка не жизнеспособна, поскольку double
не bool
, а вторая перегрузка - нет жизнеспособный, потому что T
не выводится.
По крайней мере, таковы должны быть правила . В существующей сегодня формулировке отсутствует правило, согласно которому мы переносим ограничения из каждого конструктора в набор перегрузки, и случается, что clang следует букве правил здесь - его версия __f
не имеет requires
прилагается к ним. Но это определенно не то, что мы хотим, чтобы произошло здесь, и, безусловно, будет основной проблемой. См. Также llvm bug # 44484 .
Bar
аналогичен, только с переброшенными аргументами:
template<typename T>
struct Bar
{
Bar(auto) requires same_as<T, bool>;
Bar(const T&) requires (not same_as<T, bool>);
};
Здесь единственный конструктор, который может дать нам Ответ для CTAD - второй, но второй требует, чтобы T
не было bool
. Так что Bar(true)
плохо сформирован, но Bar(3.14159)
в порядке и дает вам Bar<double>
.
Для Baz
:
template<typename T>
struct Baz
{
Baz(auto) requires same_as<T, bool>;
Baz(const auto&) requires (not same_as<T, bool>);
};
сейчас ни Конструктор участвует в CTAD, поэтому вам придется самостоятельно написать руководство по выводам, чтобы что-то сделать здесь. Отвергать это правильно.
И Qux
:
template<typename T>
struct Qux
{
Qux(T) requires same_as<T, bool>;
Qux(const T&) requires (not same_as<T, bool>);
};
Здесь оба конструктора do участвуют в CTAD, поэтому Qux(true)
и Qux(3.14159)
работают нормально (только каждый выбирает другой конструктор). Это такое же поведение, как мы видели раньше - clang следует правилам, как они, а g cc (и msv c) следуют правилам, которыми они должны быть.