Как определить, существует ли функция? - PullRequest
6 голосов
/ 10 мая 2019

Я пытаюсь определить, вызывается ли конкретная перегрузка для моей функции.Я предполагал, что мог бы сделать что-то похожее на этот ответ , но я считаю, что проблема в том, что сигнатура функции template<typename From, typename To> convert(const From&) четко определена, но экземпляр не является.

#include <iostream>
#include <string>

template<typename From, typename To>
To convert(const From& from)
{
    // I have a lot of additional template specializations for this function
    return from; 
}

template<typename From, typename To>
struct IsConvertible
{
    template<typename = decltype(convert<From, To>(From()))>
    static std::true_type test(int);
    template<typename T>
    static std::false_type test(...);

    static bool const value = decltype(test(0))::value;
};

int main()
{
    std::cout << "IsConvertible=" << IsConvertible<int, float>::value << std::endl;
    // Returns 1 as expected

    std::cout << "IsConvertible=" << IsConvertible<int, std::string>::value << std::endl;
    // Returns 1, expected 0. The issue seems to be that decltype(convert<From, To>(From()))
    // is somehow ok, although convert<int, std::string>(1) definitly isn't
}

Iхочу использовать IsConvertible для дополнительного метапрограммирования. Можно ли определить, действительно ли функция template<typename From, typename To> To convert(const From&) вызывается? `

Ответы [ 3 ]

2 голосов
/ 10 мая 2019

с декларацией

template<typename From, typename To> To convert(const From& from);

Ваши черты

template<typename From, typename To>
struct IsConvertible

всегда обнаруживает наличие функции convert.

Один из способов исправить это перегрузки и / или SFINAE:

template <typename> struct Tag{};

int convertImpl(tag<int>, const std::string& from);
float convertImpl(tag<float>, const std::string& from);
// overloads ...

template<typename From, typename To>
auto convert(const From& from)
-> decltype(convertImpl(tag<To>{}, from))
{
    return convertImpl(tag<To>{}, from);
}
1 голос
/ 10 мая 2019

Я вижу некоторые проблемы в вашем коде.

Без определенного заказа ...

(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;
 }
1 голос
/ 10 мая 2019

Возможно, я неправильно понял ваш вопрос, но недостаточно ли в этом случае использования std::is_invocable, как показано в следующем?

#include<type_traits>

template<typename From, typename To>
To convert(const From& from)
{
    // I have a lot of additional template specializations for this function
    return from; 
}

template<>
std::string convert(const int& from)
{
    //silly specialization
    return "2"+from; 
}


struct Foo{
    int bar;
};

int main()
{
   //ok specialization is called 
   std::cout<<std::is_invocable<decltype(convert<int,std::string>),std::string>::value<<std::endl; 
   //no way I can convert int to Foo, specialization required
   std::cout<<std::is_invocable<decltype(convert<int,Foo>),Foo>::value<<std::endl; 
return 0;
}
...