Доступ к типу члена с помощью `if constexpr` внутри общей лямбды требует, чтобы обе ветви были правильно сформированы — gcc vs clang

Рассмотрим два structs с псевдонимами другого типа:

struct foo { using x = int;   };
struct bar { using y = float; };

Учитывая T в template контекст, я хочу получить либо T::x или же T::y в зависимости от того, что T является:

template <typename T>
auto s()
{
auto l = [](auto p)
{
if constexpr(p) { return typename T::x{}; }
else            { return typename T::y{}; }
};

return l(std::is_same<T, foo>{});
}

int main()
{
s<foo>();
}

g++ компилирует код выше, в то время как clang++ выдает эту ошибку:

error: no type named 'y' in 'foo'
else            { return typename T::y{}; }
~~~~~~~~~~~~^
note: in instantiation of function template specialization 's<foo>' requested here
s<foo>();
^

на godbolt.org, со зрителем соответствия


Является clang++ неправильно отклонить этот код?

Обратите внимание, что clang++ принимает код при удалении косвенного обращения через общую лямбду l:

template <typename T>
auto s()
{
if constexpr(std::is_same<T, foo>{}) { return typename T::x{}; }
else                                 { return typename T::y{}; }
}

19

Решение

Увидеть Пост Ричарда Смита на std-обсуждение:

В реализации я знаком с [т.е. Clang], ключевая проблема заключается в том, что лексические области, используемые при обработке определения функции, являются в основном временными, что означает, что задержка создания экземпляра некоторой части определения шаблона функции трудно поддерживать. Общие лямбды не страдают от проблем здесь, потому что тело обобщенной лямбды создается с помощью шаблона функции включения, [..]

То есть общие тела лямбды частично создаются с использованием локального контекста (включая аргументы шаблона), когда создается экземпляр шаблона; Таким образом, в рамках реализации Clang, T::x а также T::y подставляются напрямую, поскольку тип замыкания может быть передан наружу. Это приводит к провалу. Как указывает @ T.C., Код может считаться некорректным, не требует диагностики, так как создание экземпляра s<foo> дает определение шаблона (определение замыкания), чей второй if constexpr Ветка не имеет четко сформированных экземпляров. Это объясняет поведение Clang и GCC.

Это сводится к архитектурной проблеме в основной реализации (см. Также этот ответ; GCC, очевидно, не страдает от этого ограничения), поэтому я был бы удивлен, если бы Core посчитал ваш код правильно сформированным (в конце концов, они учли это в дизайне общих лямбда-захватов — см. Связанный ответ). GCC, поддерживающий ваш код, в лучшем случае является функцией (но, вероятно, вредной, поскольку позволяет писать код, зависящий от реализации).

6

Другие решения

Других решений пока нет …

По вопросам рекламы [email protected]