C ++: Template Элемент шаблона шаблона как параметр для класса шаблона, ожидающий параметра шаблона шаблона - PullRequest
0 голосов
/ 28 мая 2020

Прежде всего, извинения за ужасное название. Я экспериментировал с функциональностью C ++ 20 is_detected. is_detected в основном принимает два параметра шаблона: один - тип более высокого порядка, который выполняет проверку, а другой - тип, который нужно проверить. Я столкнулся с проблемами в следующем сценарии:

#include <stdio.h>
#include <type_traits>

// kind of how std::experimental::is_detected is implemented
template <template <typename> typename Checker, typename T, typename = void>
struct is_detected: std::false_type {};

template <template <typename> typename Checker, typename T>
struct is_detected<Checker, T, std::void_t<Checker<T>>>: std::true_type {};

struct Foo {
    template <typename T>
    using Checker = decltype(std::declval<T>().foo());

    template <typename T>
    static constexpr void call(T &t) {
        t.foo();
    }
};

template <typename T>
using GlobalChecker = decltype(std::declval<T>().foo());

template <typename T>
struct Wrapper {
    template <typename U>
    using LocalChecker = typename T::template Checker<U>;
    //                               ^^^^^^^^ clang and msvc require template keyword
    //                                        gcc doesn't require it

    template <typename U>
    constexpr void conditional_call(U &u) const noexcept {
        if constexpr (
            is_detected<
                typename T::Checker,// !!! COMPILE ERROR !!!
                                    // works for
                                    // GlobalChecker,
                                    // LocalChecker and
                                    // Foo::Checker, though.
                std::decay_t<U>>
                ::value) {
            Foo::call(u);
        }
        else {
            puts("fallback");
        }
    }
};

int main() {
    struct {
        void foo() {
            puts("heyy!");
        }
    } t;

    Wrapper<Foo> w;
    w.conditional_call(t); // heyy! (if foo didn't exist, then fallback)
}

Если я задаю псевдоним Checker в области класса, используя using LocalChecker = typename T::template Checker<U>;, он работает; однако я хочу узнать, есть ли другой способ без использования using. Кроме того, нужен ли мне template в этом определении using? Потому что Кланг и G CC не согласны с этим.

1 Ответ

1 голос
/ 28 мая 2020

Начиная с C ++ 17, вам не нужно ключевое слово template между :: и именем специализации шаблона элемента в определенных контекстах, которые могут именовать только тип, включая спецификатор имени-типа синтаксис, который используется в шаблоне псевдонима LocalChecker. Таким образом, clang и MSV C неверно отклоняют эту строку без template и, возможно, еще не реализовали это изменение.

C ++ 14 [temp.names] / 4:

Когда имя специализации шаблона элемента появляется после . или -> в постфиксном-выражении или после вложенного-спецификатора-имени в квалифицированный-идентификатор , а объектное выражение постфиксное-выражение зависит от типа или спецификатор вложенного-имени в квалифицированный-идентификатор относится к зависимому типу, но имя не является членом текущего экземпляра (14.6.2.1), имя шаблона элемента должно начинаться с ключевого слова template. В противном случае предполагается, что имя не является шаблоном.

было заменено на C ++ 17 [temp.names] / 4 :

Ключевое слово template, как говорят, появляется на верхнем уровне в квалифицированный-идентификатор , если оно появляется за пределами списка-аргументов-шаблона или спецификатора-объявления . В квалифицированный-идентификатор из идентификатор-декларатора или в квалифицированный-идентификатор , сформированный имя-заголовка класса или enum-head-name , ключевое слово template не должно появляться на верхнем уровне. В квалифицированный-идентификатор , используемый в качестве имени в спецификатор-типа , спецификатор-разработанного-типа , объявление-использования , или class-or-decltype , необязательное ключевое слово template, появляющееся на верхнем уровне, игнорируется. В этих контекстах всегда предполагается, что токен < представляет список аргументов-шаблонов . Во всех других контекстах, при именовании специализации шаблона элемента неизвестной специализации ([temp.dep.type]), имя шаблона элемента должно начинаться с префикса ключевого слова template.

Поскольку приведенные выше правила требуют только ключевое слово template при названии специализации шаблона элемента, а не только сам шаблон элемента, может показаться, что простой T::Checker в дополнение к T::template Checker должен работать в части примера. используя is_detected. (Нам не нужно typename, поскольку мы называем шаблон псевдонима, а не тип, который является специализацией этого шаблона.) Но не ясно, почему добавление шаблона должно иметь значение, когда компилятор делает или не делает Мне не нужна помощь в определении смысла. Открытая проблема CWG 1478 связана.

В любом случае компиляторам, похоже, больше нравится подсказка template: ваша программа с is_detected<T::template Checker, ... компилируется успешно на clang ++, g ++ и msv c: см. на godbolt .

...