Подтвердить что ты не робот

Является ли это "если e является пакетом, то получите имя шаблона, иначе получите имя переменной" valid "или нет?

Я попытался построить случай, который не требует имени или шаблона, но все же дает переменную или шаблон в зависимости от того, является ли данное имя t пакетом параметров функции или нет

template<typename T> struct A { template<int> static void f(int) { } }; 
template<typename...T> struct A<void(T...,...)> { static const int f = 0; }; 
template<typename> using type = int; 

template<typename T> void f(T t) { A<void(type<decltype(t)>...)>::f<0>(1); }

int main() {
   f(1);   
}

Вышеуказанное относится к static const int и выполняет сравнение. Только что T t был изменен как пакет и сделать f ссылкой на шаблон, но GCC не нравится ни

template<typename ...T> void f(T ...t) { A<void(type<decltype(t)>...)>::f<0>(1); }

int main() {
   f(1, 2, 3);   
}

GCC жалуется на первый

main.cpp:5:68: error: incomplete type 'A<void(type<decltype (t)>, ...)>' used in nested name specifier
 template<typename T> void f(T t) { A<void(type<decltype(t)>...)>::f<0>(1); }

И для второго

 main.cpp:5:74: error: invalid operands of types '<unresolved overloaded function type>' and 'int' to binary 'operator<'
  template<typename ...T> void f(T ...t) { A<void(type<decltype(t)>...)>::f<0>(1); }

У меня есть несколько вопросов

  • Работает ли указанный выше код в соответствии с языком или есть ошибка?
  • Поскольку Clang принимает оба варианта, но GCC отклоняет, я хотел спросить, какой компилятор прав?
  • Если я удалю тело первичного шаблона, то для случая f(1, 2, 3) Clang жалуется

    main.cpp:5:42: error: implicit instantiation of undefined template 'A<void (int)>'
    

    Обратите внимание, что он говорит A<void (int) >, в то время как я ожидал A<void (int, int, int)>. Как это происходит? Является ли это ошибкой в ​​моем коде - то есть она неформальная, или это ошибка в Clang? Кажется, я помню отчет о дефекте о порядке расширения и подстановке шаблона псевдонима, является ли это актуальным и делает ли мой код плохо сформированным?

4b9b3361

Ответ 1

Расширение пакета параметров либо должно, либо делает тип зависимости зависимым. Независимо от того, расширяются ли вещи, зависит от типа.

Если бы этого не произошло, в правилах зависимостей типа С++ появлялась бы явная дыра, и это было бы дефектом в стандарте.

So A<void(type<decltype(t)>...)>::f, когда t - это пакет, независимо от того, какие трюки вы втягиваете в void( здесь ) для распаковки t, должен быть зависимым типом, а template требуется перед f, если это template.

В случае, когда t не является пакетом, предполагается, что type<decltype(t)> не зависит (см. http://www.open-std.org/jtc1/sc22/wg21/docs/cwg_active.html#1390), но стандарт может или не может согласиться на данный момент (я думаю, что нет?)

Если компиляторы сделали "то, что комитет намеревался", тогда, когда t не является пакетом:

A<void(type<decltype(t)>...)>::f<0>(1)

может означать

A<void(int...)>::f<0>(1)

который

A<void(int, ...)>::f<0>(1)

и если f является template (ваш код делает это int, но я думаю, что замена этих двух должна работать), это будет хорошо. Но стандарт, по-видимому, в настоящее время не согласен?

Итак, если http://www.open-std.org/jtc1/sc22/wg21/docs/cwg_active.html#1390, вы можете поменять свои две специализации A. Специализация void(T...,...) должна иметь template<int> void f(int), а специализация t должна иметь static const int.

Теперь в случае, когда A<> зависит (от размера пакета), ::f является int и не нуждается в template. В случае, когда A<> не зависит, ::f является template, но не нуждается в значении.

Мы можем заменить type<decltype(t)>... на:

decltype(sizeof(decltype(t)*))...

и sizeof(decltype(t)*) имеет независимый тип (это std::size_t), decltype дает нам std::size_t, а ... рассматривается как арка старой школы .... Это означает, что void(std::size_t...) становится независимым типом, поэтому A<void(std::size_t...)> не зависит, поэтому ::f является шаблоном не является шаблоном в зависимом контексте.

В случае, когда t представляет собой пакет параметров с одним элементом

decltype(sizeof(decltype(t)*))...

становится

std::size_t

но в зависимом контексте (один экземпляр на элемент в пакете t). Итак, мы получаем

A<void(std::size_t)>::f

который считается скалярным значением, поэтому

A<void(std::size_t)>::f<0>(1)

становится выражением, оценивающим false.

(Цепь логики, сгенерированная в дискуссии с Йоханнесом в комментариях в оригинальном вопросе).

Ответ 2

Ваш второй случай плохо сформирован; A<void(type<decltype(t)>...)>::f<0>(1) должен быть

A<void(type<decltype(t)>...)>::template f<0>(1)
//                             ~~~~~~~~~

В первом случае оба компилятора ведут себя некорректно; это считалось достаточно запутанным, что CWG 1520 был поднят для запроса правильного поведения; вывод заключался в том, что расширение пакета следует применять перед заменой псевдонимом:

Последняя интерпретация (список специализаций) - правильная интерпретация; пакет параметров не может быть заменен ничем, включая специализацию шаблона псевдонимов. CWG считает, что это достаточно ясно в текущей формулировке.

Это напоминает CWG 1558 (шаблоны псевдонимов и SFINAE), который был исправлен для С++ 14, но на вышеописанном уровне Ожидается, что компиляторы С++ 11 вернут это правильно, поэтому разочарование в том, что gcc и clang ошибаются (хотя, с полным основанием, они ведут себя корректно в более простых случаях, включая пример мотивации в CWG 1520). Обратите внимание, что до недавнего времени MSVC имел аналогичную ошибку; он зафиксирован в VS2015.

Ваш код (только в первом случае) правильный; но в качестве обходного пути вы можете изменить свой шаблон псевдонима для использования и отбросить его параметр шаблона, исправляя свою программу для обоих компиляторов - конечно, это означает, что ваш CWG 1390 эксплуатация перестанет быть действительной:

template<typename T> using type = decltype(((int(*)(T*))(0))(0)); // int

Однако я не думаю, что ваш трюк CWG 1390 может работать как представленный, поскольку, хотя подстановка подстановки type<decltype(t)>... не зависит от типов t..., она зависит от их числа:

template<typename T> struct A { template<int> static void f(int) {} }; 
template<> struct A<void(int, int, int)> { static const int f = 0; }; 

Как указывает Якк, его можно заставить работать, если вы поменяете шаблон функции-члена и элемент данных, поскольку член данных в порядке в зависимом контексте.