Проблема с выбором шаблона для подстановки на gcc 5

Желаемое поведение: Корректная работа функции validate.

Проблема: На версиях компилятора gcc < 9 происходит подстановка шаблона-заглушки вместо ожидаемого (т.е. не выполняется проверка условия). При этом, если раскомментировать cout (тем самым явно инстанцируя шаблон) валидатор отработает корректно. На версиях gcc 9, а так же clang (проверял на 4 и 9), код работает корректно (даже без явного инстанцирования). В чем может быть проблема?

Ссылка на godbolt

#include <iostream>
#include <type_traits>
#include <cstdint>


struct SomeParam
{
    static constexpr uint8_t default_value = 0;
    static constexpr uint8_t min = 0;
    static constexpr uint8_t max = 1;
};


template <typename T, typename = void>
struct has_max : std::false_type {};

template <typename T>
struct has_max<T, decltype (std::declval<T>().max, void())> : std::true_type {};

template <typename T, typename U>
static constexpr typename std::enable_if< has_max<T>::value, bool >::type check_max(U val)
{
    return val <= T::max;
}

template <typename T, typename U>
static constexpr typename std::enable_if< !has_max<T>::value, bool >::type check_max(U)
{
    return true;
}

template <typename Param, typename ValueType>
constexpr bool validate(ValueType value)
{
    return  check_max<Param>(value);
}



int main(int argc, char* argv[])
{
    uint8_t var = 10;
    //std::cout << has_max<SomeParam>::value;
    if ( validate<SomeParam>(var) )
        printf("HELLO");

    return 0;
}

Ответы (1 шт):

Автор решения: user7860670

В decltype (std::declval<T>().max, void()) как-то слишком много всего наворочено:

  1. std::declval используется без подключения заголовочного файла utility
  2. доступ к статическому полю через оператор точка у rvalue
  3. оператор запятой, молча отбрасывающий левое значение
  4. инстанцирование void

Более простой вариант c decltype(static_cast<void>(T::max)) работает везде: https://godbolt.org/z/fGSYtS

#include <type_traits>
#include <utility>

template <typename T, typename = void> struct
has_min: ::std::false_type {};

template <typename T> struct
//has_min<T, decltype(::std::declval<T>().min, void())>: ::std::true_type {};
has_min<T, decltype(static_cast<void>(T::min))>: ::std::true_type {};

template <typename T, typename U> constexpr
typename ::std::enable_if<has_min<T>::value, bool>::type
check_min(U val)
{
    return T::min <= val;
}

template <typename T, typename U> constexpr
typename ::std::enable_if<! has_min<T>::value, bool>::type
check_min(U)
{
    return false;
}

template <typename Param, typename ValueType> constexpr
bool validate(ValueType value)
{
    return check_min<Param>(value);
}

struct Foo1 { static constexpr int min = 5; };
struct Foo2 { static constexpr int min = 5; };
struct Foo3 { };
struct Foo4 { };

constexpr int value{6};
static_assert(has_min<Foo1>::value, "");
static_assert(validate<Foo1>(value), "");
//static_assert(has_min<Foo2>::value, "");
static_assert(validate<Foo2>(value), "");
static_assert(! has_min<Foo3>::value, "");
static_assert(! validate<Foo3>(value), "");
//static_assert(! has_min<Foo4>::value, "");
static_assert(! validate<Foo4>(value), "");
→ Ссылка