Оболочка шаблона шаблона Variadic: странные ошибки компилятора, возможно, ошибки

За годы практики метапрограммирования шаблонов я столкнулся со всевозможными странными багами и ошибками компилятора. Но с этим, я должен сказать, что я несколько озадачен. Я понятия не имею, какой компилятор правильный: gcc, clang, msvc и intel дают разные результаты (и, как ни удивительно, только intel компилирует код без ошибок). Что еще более удивительно, он не зависит от какой-либо новой функции C++, поскольку задействован только C++11.


Код будет говорить сам за себя, так как он относительно прост. Он состоит только из оболочки шаблона шаблона с переменным числом аргументов и внутреннего объекта с переменным числом переменных, который может быть либо структурой, либо шаблоном псевдонима. И по какой-то причине версия шаблона псевдонима возвращает ошибку для некоторых компиляторов при создании его экземпляра:

#include <type_traits>

template <class T1, class T2, class T3> 
struct ternary {};

template <template <class...> class Template, class... Types>
struct template_struct {
    template <class... Args>
    struct type: Template<Types..., Args...> {};
};

template <template <class...> class Template, class... Types>
struct template_alias {
    template <class... Args>
    using type = Template<Types..., Args...>;
};

А теперь тест:

int main(int, char**) {
    using ts0 = template_struct<ternary>;                           // OK
    using ts1 = template_struct<ternary, bool>;                     // OK
    using ts2 = template_struct<ternary, bool, char>;               // OK
    using ts3 = template_struct<ternary, bool, char, int>;          // OK
    using ts4 = template_struct<ternary, bool, char, int, double>;  // OK
    ts0 s0;     // OK
    ts1 s1;     // OK
    ts2 s2;     // OK
    ts3 s3;     // OK    
    ts4 s4;     // OK
    using ta0 = template_alias<ternary>;                            // OK
    using ta1 = template_alias<ternary, bool>;                      // OK
    using ta2 = template_alias<ternary, bool, char>;                // OK
    using ta3 = template_alias<ternary, bool, char, int>;           // OK
    using ta4 = template_alias<ternary, bool, char, int, double>;   // OK
    ta0 a0;     // OK
    ta1 a1;     // OK
    ta2 a2;     // OK
    ta3 a3;     // GCC, INTEL => OK | CLANG, MSVC => ERROR: WAIT WHAT ?!?!
    ta4 a4;     // INTEL => OK | GCC, CLANG, MSVC => ERROR
    return 0;
}

Код доступен в проводнике компилятора: https://godbolt.org/z/3ndYMWvfs


ВОПРОС: Что происходит? Какой компилятор правильный? Что говорит стандарт? Это ошибка компилятора?

Стоит ли изучать PHP в 2026-2027 годах?
Стоит ли изучать PHP в 2026-2027 годах?
Привет всем, сегодня я хочу высказать свои соображения по поводу вопроса, который я уже много раз получал в своем сообществе: "Стоит ли изучать PHP в...
Поведение ключевого слова "this" в стрелочной функции в сравнении с нормальной функцией
Поведение ключевого слова "this" в стрелочной функции в сравнении с нормальной функцией
В JavaScript одним из самых запутанных понятий является поведение ключевого слова "this" в стрелочной и обычной функциях.
Приемы CSS-макетирования - floats и Flexbox
Приемы CSS-макетирования - floats и Flexbox
Здравствуйте, друзья-студенты! Готовы совершенствовать свои навыки веб-дизайна? Сегодня в нашем путешествии мы рассмотрим приемы CSS-верстки - в...
Тестирование функциональных ngrx-эффектов в Angular 16 с помощью Jest
В системе управления состояниями ngrx, совместимой с Angular 16, появились функциональные эффекты. Это здорово и делает код определенно легче для...
Концепция локализации и ее применение в приложениях React ⚡️
Концепция локализации и ее применение в приложениях React ⚡️
Локализация - это процесс адаптации приложения к различным языкам и культурным требованиям. Это позволяет пользователям получить опыт, соответствующий...
Пользовательский скаляр GraphQL
Пользовательский скаляр GraphQL
Листовые узлы системы типов GraphQL называются скалярами. Достигнув скалярного типа, невозможно спуститься дальше по иерархии типов. Скалярный тип...
2
0
49
1
Перейти к ответу Данный вопрос помечен как решенный

Ответы 1

Ответ принят как подходящий

Все компиляторы правы, два не пройденных теста - это неправильно сформированный отчет о недоставке ("Диагностика не требуется").

Случай аргументов > 3 является неправильным отчетом о недоставке из-за [temp.res.general]/6.1:

The program is ill-formed, no diagnostic required, if:

— no valid specialization can be generated for a template ... and the template is not instantiated, ...

Случай аргументов == 3 является неправильным отчетом о недоставке из-за [temp.res.general]/6.3:

The program is ill-formed, no diagnostic required, if:

— every valid specialization of a variadic template requires an empty template parameter pack ...

Два теста template_struct<ternary, ...> кажутся законными, потому что вы можете специализировать struct type на что-то действительное, и я не сразу вижу правило, согласно которому должна быть хотя бы одна действительная специализация основного (неспециализированного) шаблона.

Посмотрите отредактированные комментарии, которые я сделал к коду, чтобы было понятнее: все компиляторы не могут быть правы одновременно, потому что их вывод отличается. GCC и INTEL подходят для случая == 3, а CLANG и MSVC — НЕ.

Vincent 08.05.2022 20:35

@ Винсент Нет, я правильно понял исходные комментарии, и мой ответ остается прежним. «Неправильно сформированный отчет о недоставке» (он же «диагностика не требуется») означает, что код недействителен, но не выдавать ошибку на нем разрешено.

HolyBlackCat 08.05.2022 21:14

Другие вопросы по теме