当函数不存在时 SFINAE 回退
SFINAE fallback when a function does not exist
我目前正在尝试实现一个 toString
函数,该函数调用 .toString()
或 std::to_string()
取决于哪个可用于推导类型
到目前为止我有这个工作片段:
#include <iostream>
#include <string>
template <class T>
auto toString(const T& obj)
-> decltype(obj.toString(), std::string())
{
return obj.toString();
}
template <class T>
auto toString(const T& obj)
-> decltype(std::to_string(obj), std::string())
{
return std::to_string(obj);
}
template <class T>
auto toString(const T& obj)
-> decltype(std::string(obj))
{
return std::string(obj);
}
class Foo{
public:
std::string toString() const {
return "Hello";
}
};
int main()
{
Foo bar;
std::cout << toString(bar);
std::cout << toString(5);
std::cout << toString("Hello const char*");
}
现在我想插入一个 static_assert
当上面的那些重载不可行时,因为旧 GCC 版本的默认 GCC 错误消息不是很有用。
如何检查 .toString()
和 std::to_string()
是否都不能用于 T
?
到目前为止,我找不到检查某些东西是否不的方法,只有相反的方法。我希望有人知道如何解决这个问题,感谢您的宝贵时间。
namespace details{
template<template<class...> class, class, class...>
struct can_apply : std::false_type{};
template<template<class...> class Z, class...Ts>
struct can_apply<Z, std::void_t<Z<Ts...>>, Ts...> : std::true_type{};
}
template<template<class...> class Z, class...Ts>
using can_apply = details::can_apply<Z, void, Ts...>;
template<class T>
using dot_toString_r = decltype(std::declval<T>().toString());
template<class T>
using can_dot_toString = can_apply<dot_toString_r, T>;
我留下 can_std_to_string
作为练习。
如果您的标准版本中缺少 void_t
:
template<class...> struct voider { using type=void; };
template<class...Ts> using void_t = typename voider<Ts...>::type;
即使在早期的 c++11 编译器中也能正常工作。
你需要引入一个比你目前拥有的其他任何一个都更糟糕的过载,并将其删除。您不需要检查这两个字符串函数是否都存在。
一种流行的方法是使用 C 风格的可变参数:
std::string toString(...) = delete;
您还可以使用 static_assert
和自定义错误消息:
class Dummy
{
public:
std::string toString() const;
private:
Dummy() = default;
};
template <typename... Ts>
auto toString(Ts...)
{
static_assert(std::is_same<std::tuple<Ts...>, std::tuple<Dummy>>::value, "neither std::to_str nor member toString() exists");
return "";
}
我目前正在尝试实现一个 toString
函数,该函数调用 .toString()
或 std::to_string()
取决于哪个可用于推导类型
到目前为止我有这个工作片段:
#include <iostream>
#include <string>
template <class T>
auto toString(const T& obj)
-> decltype(obj.toString(), std::string())
{
return obj.toString();
}
template <class T>
auto toString(const T& obj)
-> decltype(std::to_string(obj), std::string())
{
return std::to_string(obj);
}
template <class T>
auto toString(const T& obj)
-> decltype(std::string(obj))
{
return std::string(obj);
}
class Foo{
public:
std::string toString() const {
return "Hello";
}
};
int main()
{
Foo bar;
std::cout << toString(bar);
std::cout << toString(5);
std::cout << toString("Hello const char*");
}
现在我想插入一个 static_assert
当上面的那些重载不可行时,因为旧 GCC 版本的默认 GCC 错误消息不是很有用。
如何检查 .toString()
和 std::to_string()
是否都不能用于 T
?
到目前为止,我找不到检查某些东西是否不的方法,只有相反的方法。我希望有人知道如何解决这个问题,感谢您的宝贵时间。
namespace details{
template<template<class...> class, class, class...>
struct can_apply : std::false_type{};
template<template<class...> class Z, class...Ts>
struct can_apply<Z, std::void_t<Z<Ts...>>, Ts...> : std::true_type{};
}
template<template<class...> class Z, class...Ts>
using can_apply = details::can_apply<Z, void, Ts...>;
template<class T>
using dot_toString_r = decltype(std::declval<T>().toString());
template<class T>
using can_dot_toString = can_apply<dot_toString_r, T>;
我留下 can_std_to_string
作为练习。
如果您的标准版本中缺少 void_t
:
template<class...> struct voider { using type=void; };
template<class...Ts> using void_t = typename voider<Ts...>::type;
即使在早期的 c++11 编译器中也能正常工作。
你需要引入一个比你目前拥有的其他任何一个都更糟糕的过载,并将其删除。您不需要检查这两个字符串函数是否都存在。
一种流行的方法是使用 C 风格的可变参数:
std::string toString(...) = delete;
您还可以使用 static_assert
和自定义错误消息:
class Dummy
{
public:
std::string toString() const;
private:
Dummy() = default;
};
template <typename... Ts>
auto toString(Ts...)
{
static_assert(std::is_same<std::tuple<Ts...>, std::tuple<Dummy>>::value, "neither std::to_str nor member toString() exists");
return "";
}