如何检测功能是否存在?

时间:2019-05-10 06:57:41

标签: c++ c++11 templates metaprogramming template-meta-programming

我正在尝试检测函数的特定重载是否可调用。我以为我可以做类似于this answer的事情,但是我相信问题在于函数签名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
}

我想使用IsConvertible进行一些其他的元编程。 是否可以检测到template<typename From, typename To> To convert(const From&)函数是否真正可以调用?`

3 个答案:

答案 0 :(得分:2)

带有声明

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 :(得分:1)

我可能会误解了您的问题,但是使用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;
}

答案 2 :(得分:1)

我在您的代码中看到了一些问题。

没有特定顺序...

(1)SFINAE使用decltype()仅检查已声明函数的存在;不会检查该函数是否已定义,或者它的定义是否有效(编译)。

我建议您仅在可编译时直接使用SFINAE重写convert()声明

template <typename To, typename From,
          decltype( To(std::declval<From>()), bool{} ) = true>
To convert (From const & f)
 { return f; }

仅当您可以从convert()对象开始构造To对象时,才声明From

(2)观察到我也切换了ToFrom的顺序:这样,您可以调用convert()函数,仅说明To类型< / p>

convert<float>(0); // From is deduced as int from the 0 value

如果在To之后(不可推论)声明From(不可推论),则必须显式地同时调用{@ 1}类型是可推论的。

(3)您的From结构无效。

这是使用SFINAE的常见错误。

写作时

IsConvertible

您正尝试通过SFINAE在template<typename = decltype(convert<From, To>(From()))> static std::true_type test(int); test()上使用SFINAE启用/禁用此方法,这些方法是 struct

的模板参数

错。

SFINAE处理方法本身的模板参数。

如果要使用SFINAE,则必须在方法的模板参数中转换FromTo;例如

From

现在SFINAE使用To方法的模板参数template <typename F = From, typename T = To, typename = decltype(convert<F, T>(std::declval<F>()))> static std::true_type test(int); F,这是正确的。

(4)请注意,我写的是T而不是test()。这是因为您不确定std::declval<F>()F())是否可默认构造。使用F可以解决这个问题。

我提出了一个不同的From自定义类型特征,该特征包括std::declval() / IsConvertible的倒置和对From的{​​{1}}调用的需求{ {1}} + To-> value + test()类型转换

From

(5)您期望的是

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; }; ,切换IsConvertible<int, std::string>::value std::string)应为1。

以下是正确的完整工作示例

int