Gcc и clang не согласны с использованием шаблонов псевдонимов в качестве аргумента шаблона шаблона

Код ниже компилируется с использованием gcc 14.1.1, но clang 18.1.8 его не принимает. Какой из них правильный и есть ли обходной путь?

template <template <typename...> class T>
struct s {
    template <typename... U>
    using type = T<U...>;
};

/* the following definition of c and a are defined by the users of my library; they cannot be part of the workaround */
template <typename T>
class c {};

template <typename T>
using a = c<T>;
/* end of "user-defined" part */

int main()
{
    typename s<c>::type<int>{};
    typename s<a>::type<int>{}; // error, see bellow
}
❯ clang++ -Wall -std=c++23 -pedantic text.cpp -o test                                 
test.cpp:7:20: error: pack expansion used as argument for non-pack parameter of alias template
    7 |     using type = T<U...>;
      |                    ^~~~

Примечания и связанные вопросы

  1. Если сделать шаблон псевдонима переменным, он будет компилироваться с clang, но псевдонимы шаблонов создаются пользователями, и библиотека не имеет над ними контроля (см. код).

  2. Случай, описанный в разделах Распаковка пакетов параметров в псевдонимах шаблона и Расширение пакета для шаблона псевдонима завершается сбоем как с GCC, так и с clang. Однако может случиться так, что основная причина та же, и предоставленное решение использует механизм, аналогичный ответу здесь. Код в этом вопросе работает с GCC и не работает только с clang. Возможно, это связано с использованием аргументов шаблона шаблона.

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

Это CWG 1430

user12002570 22.07.2024 16:02

Пожалуйста, проведите тщательное исследование, прежде чем задавать какие-либо вопросы, как рекомендует кнопка «понизить голос». Просто скопируйте/вставьте ошибку, и вы обнаружите множество обманов и обходных путей. Распаковка пакетов параметров в псевдонимах шаблона и Расширение пакета для шаблона псевдонимов

user12002570 22.07.2024 16:03

@user12002570 user12002570, спасибо за предоставление ссылок на потенциальные дубликаты. Прежде чем написать свой вопрос, я поискал и увидел эти вопросы и многое другое. Я не смог соединить их вместе, чтобы решить эту проблему. Я отредактировал свое сообщение и упомянул ссылки, которые вы предоставили, с объяснением того, почему я не смог решить эту проблему, используя их. Я думаю, что этот вопрос и данный ответ могут помочь другим, кто столкнулся с аналогичной конкретной ситуацией.

R.J 22.07.2024 16:54

@user12002570 user12002570 Внимательно прочитав связанные дубликаты, я могу подтвердить, что предложенные в них обходные пути не подходят для этого ОП. Я не буду использовать свой дафаммер, чтобы опровергнуть этот вопрос, так как чувствую, что могу быть предвзятым (как автор единственного ответа). Но, пожалуйста, подумайте об этом. Голосование за возобновление работы. (также спасибо за CWG).

YSC 24.07.2024 11:55

@YSC Поскольку фундаментальные проблемы те же, и ваше решение уже решило конкретную проблему OP, лучше оставаться рядом и связываться с обманщиками. Пожалуйста.

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

Ответы 1

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

Что говорит Стандарт

Кто из них прав [...]?

Ни один. Или оба.

При первом прочтении я подумал, что clang — это неправильно, поскольку: шаблон класса и шаблон псевдонима — синонимы1; оба они могут использоваться как аргументы шаблона для шаблона параметры шаблона взаимозаменяемо2.

Но комментаторы справедливо отметили, что это ничего не доказывает. После более внимательного прочтения Стандарта я пришел лишь к выводу, что норма не дает должного ответа на этот вопрос. GCC применяет то, чего ожидаем мы, разработчики, в то время как clang придерживается более консервативного прочтения правил.

Это, как указал пользователь 1200257, CWG 1430:

Первоначально расширение пакета не могло расширяться до списка параметров шаблона фиксированной длины, но это было изменено в N2555. Это отлично работает для большинства шаблонов, но вызывает проблемы с шаблонами псевдонимов.


Обходной путь

[Есть ли] какое-нибудь обходное решение?

Следуя пословице:

Любую проблему можно решить, добавив уровень косвенности... за исключением слишком большого количества уровней косвенности.

#include <functional>

template<template <class...> class T, class... U>
struct pack
{ using type = T<U...>; };

template <template <typename...> class T>
struct s
{
    template <typename... U>
    using type = pack<T, U...>::type;
};

template <typename T>
class c {};

template <typename T>
using a = c<T>;

int main()
{
    static_assert(std::is_same_v<c<int>, s<c>::type<int>>, "template-class");
    static_assert(std::is_same_v<c<int>, s<a>::type<int>>, "template-alias");
}

=> Демо в Compiler Explorer <=


1[dcl.typedef]/1

Имя, объявленное с помощью спецификатора typedef, становится именем typedef. Имя typedef называет тип, связанный с идентификатором ([dcl.decl]) или простым идентификатором шаблона ([temp.pre]); Таким образом, имя typedef является синонимом другого типа. Typedef-name не вводит новый тип, как это делает объявление класса ([class.name]) или объявление перечисления ([dcl.enum]).

2[temp.arg]/1

Аргументом шаблона для параметра шаблона шаблона должно быть имя шаблона класса или шаблона псевдонима, выраженное как выражение id. При сопоставлении аргумента шаблона с соответствующим параметром учитываются только основные шаблоны; частичные специализации не учитываются, даже если их списки параметров совпадают со списком параметров шаблона шаблона.

Похоже, clang рассматривает шаблон псевдонима как частичный. Если я изменю псевдоним шаблона на частичный, GCC выдаст аналогичную ошибку. c++ template <typename T, typename U> class c {}; template <typename U> using a = c<int, U>;

R.J 22.07.2024 14:30

Я не думаю, что это так просто. Напоминает мне cplusplus.github.io/CWG/issues/1244.html (1) говорит об эквивалентности типов, а не об эквивалентности шаблонов, поэтому он не благословляет автоматически код OP.

HolyBlackCat 22.07.2024 15:38

@YSC, круто. Он работает даже с частичными псевдонимами шаблонов. Спасибо,

R.J 22.07.2024 15:55

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