Я вижу некоторые проблемы в вашем коде.
Без определенного заказа ...
(1) SFINAE, используя decltype()
, проверять только наличие объявленного Функция ;не проверяет, определена ли эта функция или ее определение работает (компилируется) или нет.
Я предлагаю вам переписать convert()
, используя непосредственно SFINAE, чтобы объявить только тогда, когда она компилируется
template <typename To, typename From,
decltype( To(std::declval<From>()), bool{} ) = true>
To convert (From const & f)
{ return f; }
Таким образом, convert()
объявляется, только если вы можете построить To
объект, начиная с From
объекта.
(2) Обратите внимание, что я также переключилсяпорядок To
и From
: таким образом вы можете вызвать функцию convert()
, объясняя только тип To
convert<float>(0); // From is deduced as int from the 0 value
Если вы объявите To
(это не вычитается)после From
(который выводим) вы должны явно указывать оба типа, вызывая функцию, в том числе, когда тип From
выводим.
(3) Ваша IsConvertible
структура не работает.
Это распространенная ошибка при использовании SFINAE.
Когда вы пишете
template<typename = decltype(convert<From, To>(From()))>
static std::true_type test(int);
, вы пытаетесь включить / отключить этот test()
метод с использованием SFINAE вместо From
и To
, которые являются параметрами шаблона структуры struct
Wrong.
SFINAE работает над параметрами шаблона самого метода.
Если вы хотите использовать SFINAE, вам необходимо преобразовать From
и To
в параметры шаблона метода;Например,
template <typename F = From, typename T = To,
typename = decltype(convert<F, T>(std::declval<F>()))>
static std::true_type test(int);
Теперь SFINAE использует F
и T
, которые являются параметрами шаблона метода test()
, и это правильно.
(4) Обратите внимание, что янаписано std::declval<F>()
вместо F()
.Это потому, что вы не уверены, что F
(From
) является конструируемым по умолчанию.С std::declval()
вы решаете эту проблему.
Я предлагаю другие IsConvertible
черты нестандартного типа, которые учитывают инверсию From
/ To
и требуют для вызова value
test()
преобразование типа From
+ To
-> F
+ T
template <typename To, typename From>
struct IsConvertible
{
template <typename T, typename F,
typename = decltype(convert<T>(std::declval<F>()))>
static std::true_type test(int);
template <typename...>
static std::false_type test(...);
static bool const value = decltype(test<To, From>(0))::value;
};
(5) вы ожидаете, что
IsConvertible<int, std::string>::value
равно нулю;но вы забываете, что std::string
можно построить из int
;поэтому это значение (или IsConvertible<std::string, int>
, переключение To
и From
) должно равняться единице.
Ниже приведен исправленный полный рабочий пример
#include <iostream>
#include <string>
#include <vector>
template <typename To, typename From,
decltype( To(std::declval<From>()), bool{} ) = true>
To convert (From const & f)
{ return f; }
template <typename To, typename From>
struct IsConvertible
{
template <typename T, typename F,
typename = decltype(convert<T>(std::declval<F>()))>
static std::true_type test(int);
template <typename...>
static std::false_type test(...);
static bool const value = decltype(test<To, From>(0))::value;
};
int main ()
{
std::cout << "IsConvertible=" << IsConvertible<float, int>::value
<< std::endl;
std::cout << "IsConvertible=" << IsConvertible<int, std::string>::value
<< std::endl;
}