Как реализовать обобщенную форму std :: same_as (то есть для более чем двух параметров типа), которая не зависит от порядка параметров? - PullRequest
6 голосов
/ 06 ноября 2019

Фон

Мы знаем, что понятие std::same_as не зависит от порядка (другими словами, симметрично): std::same_as<T, U> эквивалентно std::same_as<U, T> ( связанный вопрос ). В этом вопросе я хотел бы реализовать нечто более общее: template <typename ... Types> concept same_are = ..., который проверяет, равны ли типы в пакете Types друг другу.

Моя попытка

#include <type_traits>
#include <iostream>
#include <concepts>

template <typename T, typename... Others>
concept same_with_others = (... && std::same_as<T, Others>);

template <typename... Types>
concept are_same = (... && same_with_others<Types, Types...>);

template< class T, class U> requires are_same<T, U>
void foo(T a, U b) {
    std::cout << "Not integral" << std::endl;
}

// Note the order <U, T> is intentional
template< class T, class U> requires (are_same<U, T> && std::integral<T>)
void foo(T a, U b) {
    std::cout << "Integral" << std::endl;
}

int main() {
    foo(1, 2);
    return 0;
}

(Мое намерение здесь состоит в том, чтобы перечислять все возможные упорядоченные пары типов в пакете)

К сожалению, этот код не скомпилирует , так как компилятор жалуется, что вызов foo(int, int) неоднозначен,Я считаю, что он считает are_same<U, T> и are_same<T, U> не эквивалентными. Я хотел бы знать, почему код не работает, как я могу это исправить (чтобы компилятор рассматривал их как эквивалентные)?

Ответы [ 2 ]

4 голосов
/ 08 ноября 2019

Проблема заключается в следующем:

template <typename T, typename... Others>
concept are_same = (... && std::same_as<T, Others>);

Является ли нормализованная форма этого понятия ... именно этим. Мы не можем «раскрыть» это (ничего не поделаешь), и текущие правила не нормализуются через «части» концепции.

Другими словами, для этого нужноВаша концепция нормализовать в:

... && (same-as-impl<T, U> && same-as-impl<U, T>)

в:

... && (is_same_v<T, U> && is_same_v<U, T>)

И рассмотреть одно ограничение сложения-выражения &&, чтобы включить другое ограничение выражения-складки &&, если оно лежит в основе ограничениявключает в себя основное ограничение другого. Если бы у нас было это правило, это помогло бы вашему примеру работать.

Возможно, это удастся добавить в будущем - но проблема правил подстановки заключается в том, что мы не хотим, чтобы компиляторы выкладывались полностьюи реализовать полный решатель SAT, чтобы проверить включение ограничений. Не похоже, что это делает его намного более сложным (мы бы просто добавили правила && и || через выражения сгиба), но я действительно понятия не имею.

Примечаниеоднако, что даже если бы у нас было такое подчинение выражению сгиба, are_same<T, U> все равно не включало бы std::same_as<T, U>. Это будет только включать are_same<U, T>. Я не уверен, возможно ли это вообще.

3 голосов
/ 06 ноября 2019

С cppreference.com Constraint_normalization

Нормальной формой любого другого выражения E является атомное ограничение, выражение которого E, а сопоставление параметров которого - отображение идентификаторов. Это включает в себя все выражения сгиба, даже те, которые складываются поверх && или ||операторы.

Итак

template <typename... Types>
concept are_same = (... && same_with_others<Types, Types...>);

является "атомарным".

Так что действительно are_same<U, T> и are_same<T, U> не эквивалентны.

Я не вижу, как это реализовать: - (

...