当前位置: 首页 > 知识库问答 >
问题:

SFINAE:知道函数是否已经存在

萧浩漫
2023-03-14

基本上,我想写这样的代码:

std::vector<float> a = { 54, 25, 32.5 };
std::vector<int> b = { 55, 65, 6 };

std::cout << a << b << std::string("lol");

这是不可能的,因为操作符<<(ostream&,vector)没有重载

template<template<typename...> typename T, typename ...Args>
std::enable_if_t<is_iterable_v<T<Args...>>>, std::ostream> &operator<<(std::ostream &out, T<Args...> const &t) {
    for (auto const &e : t)
        out << e << " ";
    out << std::endl;
    return out;
}

因此,我目前正在使用!is_same_v ,但它并不完美。

有没有一种方法可以不重新声明函数就知道函数是否存在?

基本上,我想做一些类似的事情

if function foo does not exist for this type.
then function foo for this type is ...
template <class...>
struct make_void { using type = void; };

template <typename... T>
using void_t = typename make_void<T...>::type; // force SFINAE

namespace detail {
    template<typename AlwaysVoid, template<typename...> typename Operator, typename ...Args>
    struct _is_valid : std::false_type {};


    template<template<typename...> typename Operator, typename ...Args>
    struct _is_valid<void_t<Operator<Args...>>, Operator, Args...> : std::true_type { using type = Operator<Args...>; };
}

template<template<typename ...> typename Operator, typename ...Args>
using is_valid = detail::_is_valid<void, Operator, Args...>;

#define HAS_MEMBER(name, ...)\
template<typename T>\
using _has_##name = decltype(std::declval<T>().name(__VA_ARGS__));\
\
template<typename T>\
using has_##name = is_valid<_has_push_back, T>;\
\
template<typename T>\
constexpr bool has_##name##_v = has_##name<T>::value

HAS_MEMBER(push_back, std::declval<typename T::value_type>());
HAS_MEMBER(begin);
HAS_MEMBER(end);

template<typename T>
using is_iterable = std::conditional_t<has_begin_v<T> && has_end_v<T>, std::true_type, std::false_type>;

template<typename T>
constexpr bool is_iterable_v = is_iterable<T>::value;



template<class T, class Stream, class = void>
struct can_print : std::false_type {};

template<class T, class Stream>
struct can_print<T, Stream, void_t<decltype(std::declval<Stream&>() << std::declval<const T&>())>> : std::true_type {};

template<class T, class Stream = std::ostream>
constexpr bool can_print_v = can_print<T, Stream>::value;

template<typename T>
std::enable_if_t<is_iterable_v<T> && !can_print_v<T>, std::ostream> &operator<<(std::ostream &out, T const &t) {
    for (auto const &e : t)
        out << e << " ";
    out << std::endl;
    return out;
}

template<typename A, typename B>
std::ostream &operator<<(std::ostream &out, std::pair<A, B> const &p) {
    out << p.first << " " << p.second << std::endl;
    return out;
}

template<typename T>
std::enable_if_t<has_push_back_v<T>, T> &operator<<(T &c, typename T::value_type const &e) {
    c.push_back(e);
    return c;
}

和主要的:

#include <iostream>
#include <vector>
#include "Tools/stream.h"
#include <string>
#include <map>

int main() {
    std::vector<float> a = { 54, 25, 32.5 };
    std::vector<int> b = { 55, 65, 6 };

    std::cout << a;

    std::cout << has_push_back<float>::value  << " " << has_push_back<std::vector<float>>::value << std::endl;
    std::cout << is_iterable<std::vector<float>>::value << " " << is_iterable<float>::value << std::endl;

    getchar();
    return 0;
}

共有1个答案

关志勇
2023-03-14

您可以编写一个小的检测习惯用法来测试表达式流< 是否格式良好*。

这里使用的是std::ostream:

template<class...>
using void_t = void;

template<class T, class Stream, class=void>
struct can_print : std::false_type{};

template<class T, class Stream>
struct can_print<T, Stream, void_t<decltype(std::declval<Stream&>() << std::declval<const T&>())>> : std::true_type{};

template<class T, class Stream=std::ostream>
constexpr bool can_print_v = can_print<T, Stream>::value;

现在可以为运算符<<编写重载,如下所示:

template<template<class...> class C, class...T>
std::enable_if_t<!can_print_v<C<T...>>, std::ostream>& operator<<(std::ostream &out, C<T...> const &t) {
    for (auto const &e : t)
        out << e << " ";
    out << std::endl;
    return out;
}
std::vector<float> a = { 54, 25, 32.5 };
std::vector<int> b = { 55, 65, 6 };
std::cout << a;
std::cout << b;
std::cout << std::string("lol") << std::endl;

*似乎OP已经将此代码复制到他们自己的代码库中,然后修改问题以包括它,以防您想知道为什么它看起来相反

 类似资料:
  • 客户端连接到服务器,并使用头发送请求。服务器响应还包含header。 现在客户断电了。TCP或HTTP堆栈中是否有任何机制(如ping)告诉服务器客户端不在那里(除了超时)?

  • 问题内容: 我使用的是Font-Awesome,但未加载字体文件时,图标显示为。 因此,我希望这些图标在未加载文件时具有。 我怎么知道这些文件已经加载,我终于可以显示图标了? 编辑: 我不是在说页面何时加载(onload),因为字体可以在整个页面之前加载。 问题答案: 现在在GitHub上: 本质上,该方法通过比较两种不同字体的字符串宽度来工作。我们使用Comic Sans作为测试字体,因为它是

  • 问题内容: 是否可以 不 尝试加载就知道是否已加载Java类?尝试加载该类,但我不希望出现这种副作用。还有另一种方法吗? (我不想覆盖类加载器。我正在寻找一个相对简单的方法。) 问题答案: (感谢Aleksi)此代码: 产生: 请注意,示例类不在软件包中。完整的二进制名称是必需的。 二进制名称的一个示例是

  • 问题内容: 我的应用程序的Web UI层中有一个JPA实体实例。我想随时知道此实体是否已经保存在数据库中或仅存在于用户会话中。 它将在业务层中,我将使用entitymanager.contains(Entity)方法,但是在我的UI层中,我认为我需要一个额外的属性来指示实体是否已保存。如何实施?我目前正在考虑以下选择: 由数据库设置默认值的JPA属性,但是每次更新后是否会强制进行新的读取? 在我的

  • 问题内容: 我正在使用拦截器在基于Struts的应用程序中实现一些功能,而对其生命周期的工作方式却感到困惑。根据Struts的文档(“拦截器”,“写拦截器”和“大图”),它应该像这样工作: 这是有道理的,但是我在如何区分在操作之前执行的拦截器调用与在结果呈现之后执行的拦截器调用之间进行了尝试(我在这里跳过了s)。 如果启动调试器,则会有两个调用, 并且在通过时找不到任何明显的内容 。 ( 更新 :