SFINAE легко использовать, чтобы скрыть перегрузку определенной функции, если конкретное выражение имеет неправильный формат. Но я хочу сделать обратное, скрыв перегрузку тогда и только тогда, когда заданное выражение является правильным, и сделать это в очень общем виде. У меня есть решение, которое работает в clang 3.5.0 и gcc 5.2.0, но меня интересуют любые комментарии и альтернативы.
В идеале должна быть встроенная constexpr bool
функция/макрос, которая сообщала бы нам во время компиляции, правильно ли построено конкретное выражение.
IS_WELL_FORMED( declval<T>() < declval<T>() ) // I want this as bool
который можно использовать с enable_if
для включения или отключения перегрузок.
Я нашел решение, но я столкнулся с каким-то странным поведением в g++ 5.2.0 и clang 3.5.0, и мне интересно, есть ли ошибки.
Предлагаемое решение
Во-первых, самое надежное решение, которое я нашел до сих пор, которое работает на обоих компиляторах. Например, я хочу проверить, есть ли у T
метод .length()
. Это требует «скрытия» выражения внутри другого шаблона. Кроме того, функция под названием well_formed_default
, о которой я расскажу позже.
// Define a template to contain our expression
template<typename T2=T, typename =
decltype( declval<T2>().length() ) // This line is the expression to test
> struct test_for_length_method { };
и вот как его можно использовать в содержащем классе:
template<typename T>
struct Demo { // the main struct I'm working on
// Define a template to "hide" our expression
template<typename T2=T, typename =
decltype( declval<T2>().length() ) // This line is the expression to test
> struct test_for_length_method { };
// test if the above "test" succeeds
constexpr bool T_has_length =
well_formed_default< test_for_length_method >();
// demonstrate the bool in an enable_if
template<bool b = T_has_length>
static
auto demo_enable_if() -> typename std::enable_if< b >::type {
cout << "T has length" << endl;
}
template<bool b = T_has_length>
static
auto demo_enable_if() -> typename std::enable_if< !b >::type {
cout << "T doesn't" << endl;
}
}
Вышеприведенное работает, как и ожидалось, с Demo<int>::demo_enable_if()
и Demo<std::string>::demo_enable_if()
.
Я не могу использовать T_has_length
непосредственно внутри enable_if
, так как это приведет к серьезным ошибкам, потому что это не замена шаблона. Поэтому я делаю вид, что это параметр шаблона, делая копию if в другом параметре шаблона bool b = T_has_length
. Это похоже на то, как мы должны использовать typename T2=T
внутри тестовой структуры. Немного раздражает, но я думаю, что это имеет смысл.
Теперь я определяю well_formed_default
. Он принимает шаблон (и, возможно, некоторые типы) и возвращает true или false в зависимости от того, может ли он построить шаблон с этими конкретными аргументами. Я бы включил его автоматически во все свои проекты. Возможно, что-то подобное уже есть в стандарте?
template<template<class...> class Template, class ...Args>
constexpr auto well_formed_default_impl(int)
-> typename std::conditional<false,Template<Args...>, bool>::type {
return true;
}
template<template<class...> class Template, class ...Args>
constexpr auto well_formed_default_impl(...)
-> bool {
return false;
}
template<template<class...> class Template, class ...Args>
constexpr bool well_formed_default() {
return well_formed_default_impl<Template,Args...>(0);
}
--
Мой (первый) вопрос
Это работает как в g++ 5.2.0, так и в clang 3.5.0. Но должно ли? Это полностью стандарт, или я слишком далеко захожу со стандартом? Я думаю, что самое странное для меня - это использование Template<Args...>
внутри well_formed_default_impl
- гарантированно ли это будет ошибкой замены в том виде, в котором я его использую? например с test_for_pushback_method_struct<>
, когда соответствующий decltype
неправильно сформирован?
--
Почему я не могу использовать вместо этого псевдоним?
(Что касается оставшейся части этого вопроса, может быть полезно взглянуть на вывод этого кода на Coliru так как в нем есть все тесты и результаты, которые я обсуждаю ниже.)
Я начал этот проект с псевдонима вместо указанной выше структуры. Я думал, что это будет эквивалентно. Но вместо этого оба компилятора считают, что string
нет метода длины.
template<typename T2=T>
using test_for_length_method_alias = decltype( declval<T2>().length() );
Наконец, я попробовал и структуру, и псевдоним, но там, где я явно определяю первый параметр типа (T
) вместо того, чтобы полагаться на значение по умолчанию T2=T
. Это ничего не должно изменить, потому что я передаю тип по умолчанию, но это меняет поведение! Использование структуры с явным первым параметром
well_formed_default<test_for_length_method_struct , T>
корректно работает на обоих компиляторах. Но alias-with-explicit-first-type корректно работает только с clang:
well_formed_default<test_for_length_method_alias , T>
template<class...> class
неправильно соответствует шаблону псевдонима, либо замена не удалась. - person dyp   schedule 14.08.2015is_detected
выше (та часть, которая вам нужна). - person Yakk - Adam Nevraumont   schedule 14.08.2015structs
. - person Aaron McDaid   schedule 15.08.2015