如何检测函数是否存在?
How to detect if a function exists?
我正在尝试检测我的函数的特定重载是否可调用。我以为我可以做类似于 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&)
函数是否实际可调用?`
我可能误解了你的问题,但在这种情况下使用 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;
}
声明
template<typename From, typename To> To convert(const From& from);
你的特质
template<typename From, typename To>
struct IsConvertible
将始终检测 convert
函数的存在。
修复它的一种方法是重载 and/or 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) SFINAE,使用decltype()
,只检查声明的函数的存在;不检查该函数是否已定义或它的定义是否有效(编译)。
我建议您直接使用 SFINAE 重写 convert()
以 声明 它仅在可编译时
template <typename To, typename From,
decltype( To(std::declval<From>()), bool{} ) = true>
To convert (From const & f)
{ return f; }
只有当您可以从 From
对象构造一个 To
对象时,才声明 convert()
。
(2) 请注意,我还调换了 To
和 From
的顺序:这样您就可以调用 convert()
函数,仅解释 To
类型
convert<float>(0); // From is deduced as int from the 0 value
如果在 From
(可推导)之后声明 To
(不可推导),则必须明确两种类型,调用函数,当 From
类型是可推导的。
(3) 您的 IsConvertible
结构不起作用。
这是使用 SFINAE 的常见错误。
写的时候
template<typename = decltype(convert<From, To>(From()))>
static std::true_type test(int);
您正在尝试 enable/disable 这个 test()
方法使用 SFINAE 而不是 From
和 To
,它们是 struct[= 的模板参数100=]
错了。
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
自定义类型特征,它考虑了对 test()
的 value
调用的 From
/To
反转和需求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;
}
我正在尝试检测我的函数的特定重载是否可调用。我以为我可以做类似于 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&)
函数是否实际可调用?`
我可能误解了你的问题,但在这种情况下使用 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;
}
声明
template<typename From, typename To> To convert(const From& from);
你的特质
template<typename From, typename To>
struct IsConvertible
将始终检测 convert
函数的存在。
修复它的一种方法是重载 and/or 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) SFINAE,使用decltype()
,只检查声明的函数的存在;不检查该函数是否已定义或它的定义是否有效(编译)。
我建议您直接使用 SFINAE 重写 convert()
以 声明 它仅在可编译时
template <typename To, typename From,
decltype( To(std::declval<From>()), bool{} ) = true>
To convert (From const & f)
{ return f; }
只有当您可以从 From
对象构造一个 To
对象时,才声明 convert()
。
(2) 请注意,我还调换了 To
和 From
的顺序:这样您就可以调用 convert()
函数,仅解释 To
类型
convert<float>(0); // From is deduced as int from the 0 value
如果在 From
(可推导)之后声明 To
(不可推导),则必须明确两种类型,调用函数,当 From
类型是可推导的。
(3) 您的 IsConvertible
结构不起作用。
这是使用 SFINAE 的常见错误。
写的时候
template<typename = decltype(convert<From, To>(From()))>
static std::true_type test(int);
您正在尝试 enable/disable 这个 test()
方法使用 SFINAE 而不是 From
和 To
,它们是 struct[= 的模板参数100=]
错了。
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
自定义类型特征,它考虑了对 test()
的 value
调用的 From
/To
反转和需求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;
}