我正在尝试检测函数的特定重载是否可调用。我以为我可以做类似于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&)
函数是否真正可以调用?`
答案 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)观察到我也切换了To
和From
的顺序:这样,您可以调用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,则必须在方法的模板参数中转换From
和To
;例如
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