SFINAE не подходит для пользовательского шаблона.Надо понимать почему - PullRequest
1 голос
/ 30 июня 2019

Мой код должен проверять различные типы пикселей на «достоверность». Например, пиксели с плавающей запятой недопустимы, если они сообщают true для std::isnan().

Итак, у меня есть структура шаблона «валидатор», которую я специализирую для своих различных типов пикселей (здесь, только для float). Мой код использует глобальную шаблонную функцию для вызова правильной перегрузки через SFINAE

// Dummy implementation breaks compilation if no overload found.
template<class PEL, typename Enable=void> struct pixel_validator  { };


template<class PEL> 
struct pixel_validator<PEL, typename std::enable_if<std::is_floating_point<PEL>::value>::type>
{
    static bool validate(const PEL& p) { return !std::isnan(p);  }
};


template<class PEL>
inline bool is_valid_pixel(const PEL& p) 
{
    // Dispatch to validator above
    return pixel_validator<PEL>::validate(p); 
};


void main
{
     float x = 1.0f;
     std::cout << "is it valid ?" << std::boolalpha << is_valid_pixel(x);
}

И этот пример работает просто отлично. Специализация pixel_validator для float выбрана. Все хорошо.

Но затем я попытался уменьшить детализацию шаблонных выражений для ясности с помощью пользовательской версии "std::enable_if" специально для float.

template<class T, class VAL=T>
struct enable_if_floating
    : std::enable_if<std::is_floating_point<T>::value, VAL>
{};

Так что теперь вместо того, чтобы писать это:

std::enable_if<std::is_floating_point<PEL>::value>::type

Я могу написать

enable_if_floating<PEL>::value

... поэтому мой валидатор становится:

template<class PEL> 
struct pixel_validator<PEL, typename enable_if_floating<PEL>::type>
{
    static bool validate(const PEL& p) { return !std::isnan(p); }
};

К сожалению, в тот момент, когда я изменил свой "pixel_validator" на использование, код не может быть собран. Мой enable_if_floating не работает, и поэтому Visual Studio не может найти соответствующую специализацию. Мой вывод не удивителен.

1>------ Build started: Project: TestApp7, Configuration: Debug Win32 ------
1>TestApp7.cpp
1>C:\Test\TestApp7\TestApp7.cpp(62,34): error C2039:  'validate': is not a member of 'pixel_validator<PEL,void>'
1>C:\Test\TestApp7\TestApp7.cpp(62,34): error C2039:         with
1>C:\Test\TestApp7\TestApp7.cpp(62,34): error C2039:         [
1>C:\Test\TestApp7\TestApp7.cpp(62,34): error C2039:             PEL=float
1>C:\Test\TestApp7\TestApp7.cpp(62,34): error C2039:         ]
1>C:\Test\TestApp7\TestApp7.cpp(62): message :  see declaration of 'pixel_validator<PEL,void>'
1>C:\Test\TestApp7\TestApp7.cpp(62): message :         with
1>C:\Test\TestApp7\TestApp7.cpp(62): message :         [
1>C:\Test\TestApp7\TestApp7.cpp(62): message :             PEL=float
1>C:\Test\TestApp7\TestApp7.cpp(62): message :         ]
1>C:\Test\TestApp7\TestApp7.cpp(82): message :  see reference to function template instantiation 'bool is_valid_pixel<float>(const PEL &)' being compiled
1>C:\Test\TestApp7\TestApp7.cpp(82): message :         with
1>C:\Test\TestApp7\TestApp7.cpp(82): message :         [
1>C:\Test\TestApp7\TestApp7.cpp(82): message :             PEL=float
1>C:\Test\TestApp7\TestApp7.cpp(82): message :         ]
1>C:\Test\TestApp7\TestApp7.cpp(62,1): error C3861:  'validate': identifier not found
1>Done building project "TestApp7.vcxproj" -- FAILED.
========== Build: 0 succeeded, 1 failed, 0 up-to-date, 0 skipped ==========

Мой вопрос: почему? Что не так с моим enable_if_floating?

Примечание: я даже поместил этот код в main(), просто для проверки работоспособности. Если бы мой шаблон был плохим, я бы ожидал, что static_assert() потерпит неудачу, но это не так.

// Sanity check #2.  Does my enable_if_floating test  reports that float
// enables because it's numeric?  If not then the static_assert below should fail

using float_type = enable_if_floating<float>::type;
static_assert(std::is_same_v<float_type, float>, "Not same as float...");

Примечание также: мой код реального мира использует предикат, который экономит намного больше места, чем в этом простом примере

1 Ответ

2 голосов
/ 30 июня 2019

Не уверен, что это единственная проблема, но есть проблема.

Если вы пишете

template<class T, class VAL=T>
struct enable_if_floating
    : std::enable_if<std::is_floating_point<T>::value, VAL>
{};

, по умолчанию возвращается type, равное T, где для std::is_enable_if этоvoid.

Так

template<class PEL> 
struct pixel_validator<PEL, typename enable_if_floating<PEL>::type>
{
    static bool validate(const PEL& p) { return !std::isnan(p); }
};

становятся, когда PEL является типом с плавающей запятой,

template<class PEL> // .....VVV   should be void, not PEL
struct pixel_validator<PEL, PEL>
{
    static bool validate(const PEL& p) { return !std::isnan(p); }
};

, который не соответствует pixel_validatorобъявление (и основное определение)

template<class PEL, typename Enable=void>
struct pixel_validator
 { };

, потому что ожидаемый второй тип void, а не PEL.

Я вижу два возможных альтернативных решения: или вы используете void какзначение по умолчанию для enable_is_floating второй параметр шаблона

// ...........................VVVV 
template<class T, class VAL = void>
struct enable_if_floating
    : std::enable_if<std::is_floating_point<T>::value, VAL>
 { };

или вы используете PEL в качестве значения по умолчанию для pixel_validator второй параметр шаблона

template <typename PEL, typename = PEL>
struct pixel_validator
 { };

Я предлагаю первый однородныйс std::enable_if и стандартной библиотекой C ++.

...