В качестве упражнения в понимании использования std::enable_if я попытался реализовать класс-оболочку (структуру) для представления определенного типа в любой заданный момент времени:
#include<type_traits>
#include<typeinfo>
#include<iostream>
using std::enable_if;
using std::is_same;
using std::cout;
using std::endl;
template<typename T>
struct type_wrap{
type_wrap(typename enable_if<is_same<int,T>::value,T>::type&& rrT):value(rrT){
cout << "The wrapped type is " << typeid(value).name() << endl;
cout << "The wrapped value is " << value << endl;
}
type_wrap(typename enable_if<is_same<float,T>::value,T>::type && rrT):value(rrT){
cout << "The wrapped type is " << typeid(value).name() << endl;
cout << "The wrapped value is " << value << endl;
}
T& value;
};
int main(){
type_wrap<int>(0);
type_wrap<float>(0.5);
return(0);
}
Приведенный выше код не компилируется:
so_main.cpp:16:47: error: no type named 'type' in 'std::__1::enable_if<false, int>'; 'enable_if' cannot be used to disable this declaration
type_wrap(typename enable_if<is_same<float,T>::value,T>::type && rrT):value(rrT){
^~~~~~~~~~~~~~~~~~~~~~~
so_main.cpp:26:9: note: in instantiation of template class 'type_wrap<int>' requested here
type_wrap<int>(0);
^
so_main.cpp:12:47: error: no type named 'type' in 'std::__1::enable_if<false, float>'; 'enable_if' cannot be used to disable this declaration
type_wrap(typename enable_if<is_same<int,T>::value,T>::type&& rrT):value(rrT){
^~~~~~~~~~~~~~~~~~~~~
so_main.cpp:27:9: note: in instantiation of template class 'type_wrap<float>' requested here
type_wrap<float>(0.5);
^
2 errors generated.
Код работает, если я удалю один из перегруженных конструкторов и соответствующий экземпляр из main(). Но это сводит на нет всю цель этого упражнения.
Может кто-нибудь указать причину ошибки компиляции?





Can someone point out the cause for the compilation error?
Потому что std::enable_if сделает один из ваших конструкторов незаконным в зависимости от каждого:
type_wrap<int>(0);
type_wrap<float>(0.5);
int или double заставят другую сторону std::is_same иметь false, в этом случае std::enable_if не имеет type:
template<bool B, class T = void>
struct enable_if {}; // int or float will get this on each constructor.
template<class T>
struct enable_if<true, T> { typedef T type; };
Вместо этого используйте специализацию шаблона следующим образом:
template<typename T>
struct type_wrap;
template<>
struct type_wrap<float>
{
type_wrap(float&& rrT) :value(rrT) {
cout << "The wrapped type is " << typeid(value).name() << endl;
cout << "The wrapped value is " << value << endl;
}
float& value;
};
template<>
struct type_wrap<int>
{
type_wrap(int&& rrT) :value(rrT) {
cout << "The wrapped type is " << typeid(value).name() << endl;
cout << "The wrapped value is " << value << endl;
}
int& value;
};
Если ваш компилятор поддерживает C++ 17, if constexpr сделает это намного проще и понятнее:
template<typename T>
struct type_wrap
{
type_wrap(T&& rrT):value(rrT)
{
if constexpr (std::is_same<int, T>::value)
{
cout << "The wrapped type is " << typeid(value).name() << endl;
cout << "The wrapped value is " << value << endl;
}
else
{
cout << "The wrapped type is " << typeid(value).name() << endl;
cout << "The wrapped value is " << value << endl;
}
}
T& value;
};
@ user9196120 Первый конструктор недопустим, если T - это float. Второй конструктор недопустим, если T - это int. В этом случае нет SFINAE - ваш компилятор должен видеть два конструктора, действительные в обоих случаях.
SFINAE работает с методом шаблона (/ constructor), вот ваш класс, который является шаблоном, вы можете использовать следующее (даже если специализация кажется проще / лучше в вашем случае):
template<typename T>
struct type_wrap{
template <typename U,
std::enable_if_t<std::is_same<int, U>::value
&& is_same<int, T>::value>* = nullptr>
type_wrap(U arg) : value(arg){
// Int case
std::cout << "The wrapped type is " << typeid(value).name() << std::endl;
std::cout << "The wrapped value is " << value << std::endl;
}
template <typename U,
std::enable_if_t<std::is_same<float, U>::value
&& is_same<float, T>::value>* = nullptr>
type_wrap(U arg) : value(arg){
// float case
std::cout << "The wrapped type is " << typeid(value).name() << std::endl;
std::cout << "The wrapped value is " << value << std::endl;
}
T value;
};
@ user9196120: Опечатка исправлена, добавлена демоверсия.
Как вы могли заметить, type_wrap<float>(0.5) недействителен с моей версией, поскольку вы передаете double вместо float. Тогда как специализация создает нешаблонные методы, которые позволяют конвертировать / продвигать.
@@ Jarod421 Не могли бы вы дать объяснение. о том, почему вы используете * для enable_if_t и устанавливаете для него значение nullptr? Мне любопытна задействованная логика, но я не уверен, что понимаю ее.
Вы можете посмотреть почему я должен избегать stdenable-if-in-function-signatures, который показывает другой способ использования std::enable_if.
Спасибо за объяснение, но я не совсем понимаю. Насколько я понимаю, цель использования enable_if () - включить только одну перегрузку на основе конкретного экземпляра. Ваше наблюдение «int или double будет противоречить другой стороне std :: is_same, чтобы иметь false», мне не ясно. Я получаю аналогичную ошибку, даже если бы я использовал только один экземпляр, то есть int или float, в main ().