Это продолжение Другой вопрос. Это относится к той же проблеме (я надеюсь), но для ее иллюстрации используется совершенно другой пример. Причина в том, что в предыдущем примере только экспериментальный GCC 4.9 завершился с ошибкой компилятора. В этом примере также Clang и GCC 4.8.1 терпят неудачу по-разному: Clang дает неожиданный результат, а GCC 4.8.1 сообщает другое сообщение об ошибке.
Ответы на предыдущий вопрос говорят более или менее о том, что код действителен, и проблема заключается в экспериментальной версии GCC. Но этот результат делает меня немного более скептическим. Я месяцами беспокоился о проблемах, которые, как я подозреваю, связаны (или совпадают), и я впервые приведу небольшой конкретный пример для иллюстрации.
Итак, вот некоторый код. Во-первых, некоторый общий код, который применяет SFINAE к произвольному тесту, как указано в мета-функции псевдонима шаблона F
:
#include <iostream>
using namespace std;
using _true = integral_constant <bool, true>;
using _false = integral_constant <bool, false>;
template <typename T> using pass = _true;
template <template <typename...> class F>
struct test
{
template <typename... A> static _false _(...);
template <typename... A> static pass <F <A...> > _(int);
};
template <template <typename...> class F, typename... A>
using sfinae = decltype(test <F>::template _<A...>(0));
Во-вторых, конкретный тест, проверяющий, определил ли данный класс тип с именем type
:
template <typename T> using type_of = typename T::type;
template <typename T> using has_type = sfinae <type_of, T>;
Наконец, пример:
struct A { using type = double; };
int main()
{
cout << has_type <int>() << ", ";
cout << has_type <A>() << endl;
}
Ожидаемый результат будет 0, 1
, Clang говорит 0, 0
, GCC 4.8.1 говорит
tst.cpp: In substitution of ‘template<class T> using type_of = typename T::type [with T = A ...]’:
tst.cpp:15:51: required from ‘struct test<type_of>’
tst.cpp:19:67: required by substitution of ‘template<template<class ...> class F, class ... A> using sfinae = decltype (test:: _<A ...>(0)) [with F = type_of; A = {T}]’
tst.cpp:24:58: required from here
tst.cpp:23:56: error: ‘A ...’ is not a class, struct, or union type
template <typename T> using type_of = typename T::type;
^
и GCC 4.9 говорит
tst.cpp:19:67: required by substitution of ‘template<template<class ...> class F, class ... A> using sfinae = decltype (test:: _<A ...>(0)) [with F = type_of; A = {T}]’
tst.cpp:24:58: required from here
tst.cpp:15:51: error: pack expansion argument for non-pack parameter ‘T’ of alias template ‘template<class T> using type_of = typename T::type’
template <typename... A> static pass <F <A...> > _(int);
^
(номера строк могут отличаться). Итак, все терпит неудачу по-разному.
Теперь вот обходной путь. Metafunction car
выбирает первый тип из данной пачки, а затем тест переопределяется как type_of2
сейчас вария:
template <typename... T> struct car_t;
template <typename... T> using car = type_of <car_t <T...> >;
template <typename T, typename... Tn>
struct car_t <T, Tn...> { using type = T; };
template <typename... T> using type_of2 = typename car <T...>::type;
template <typename T> using has_type2 = sfinae <type_of2, T>;
int main()
{
cout << has_type2 <int>() << ", ";
cout << has_type2 <A>() << endl;
}
Теперь все три компилятора говорят 0, 1
как и ожидалось. Интересно, что для любой версии GCC мы должны удалить has_type
(даже если мы не используем его) и оставить только has_type2
; в противном случае мы имеем подобную ошибку.
Упаковать: Я вижу проблему с одним шаблоном, ожидающим переменный шаблон-параметр формы
template <typename...> class F
где мы на самом деле даем в качестве входных данных невариантный шаблон псевдоним формы
template <typename T> using alias = // ... anything including T or not
и, наконец, вызвать F
как будто это было вариадно
F <A...>
До сих пор мнения говорят, что это действительно, но сейчас, похоже, три компилятора не согласны. Итак, вопрос снова: это действительно?
Для меня это важно, потому что у меня есть десятки файлов существующего кода, основанные на предположении, что это допустимо, и теперь мне все равно нужен редизайн (так как с этими компиляторами есть практические проблемы), но точный редизайн будет зависеть от ответа.
Это не отвечает на вопрос, является ли приведенный выше код верным, но это довольно симпатичный обходной путь, который я нашел, экспериментируя вскоре после того, как задал вопрос, и я думаю, что им полезно поделиться.
Все, что нужно, это следующие определения:
template <template <typename...> class F>
struct temp { };
template <typename... A, template <typename...> class F>
F <A...> subs_fun(temp <F>);
template <template <typename...> class F, typename... A>
using subs = decltype(subs_fun <A...>(temp <F>()));
тогда, где бы F <A...>
было бы проблематично, замените его subs <F, A...>
, Вот и все. Я не могу объяснить почему, но до сих пор это работало во всех случаях.
Например, в примере SFINAE вопроса просто замените строку
template <typename... A> static pass <F <A...> > _(int);
от
template <typename... A> static pass <subs <F, A...> > _(int);
Это изменение только в одном месте, весь оставшийся код остается прежним. Вам не нужно переопределять или переносить каждую метафункцию шаблона, которая будет использоваться как F
, Вот живой пример.
Если F <A...>
действительно действует, и компиляторы его поддерживают в конце концов, снова легко переключиться обратно, потому что изменения минимальны.
Я считаю это важным, потому что оно позволяет указывать тест SFINAE всего в двух строках.
template <typename T> using type_of = typename T::type;
template <typename T> using has_type = sfinae <type_of, T>;
и является полностью общим. Как правило, для каждого такого теста требуется не менее 10 строк кода и реализации <type_traits>
полны такого кода. В некоторых случаях такие блоки кода определяются как макросы. Благодаря этому решению шаблоны могут выполнять свою работу, а макросы не нужны.
Я думаю, что ситуация довольно хорошо стандартизирована; C ++ 11 14.3.3 / 1 говорит:
Шаблон-аргумент для шаблона Шаблон-параметр должно быть именем шаблона класса или шаблона псевдонима, выраженным как ID-выражение.