c++ - 在 C++14 的 Variadic 模板中选择可调用类型和不可调用类型是否有一个优雅的解决方案

标签 c++ c++14 function-pointers variadic-templates

我正在为一个类实现简单的文本格式化程序。其中的主要功能可以接收将连接的值列表。或者可选地,对于参数不是 ostream 的 friend 的情况,我接受一个转换函数作为第一个参数,它将所有其他参数转换为 std::string。

下面的代码展示了这个想法,但它没有编译。为简单起见,我将在示例中输出到 cout。

struct formater{
    template<typename P, typename... PS>
    void format(const P& p, const PS&... ps){
        if (std::is_convertible<P, std::function<void()>>::value){
            cout << p(ps...) << endl;
        } else {
            cout << p;
            log(ps...);
        }
    }
};

代码不编译的原因是,如果 P 是可调用的,将无法在“else”分支中将其输出到 cout,如果 P 不可调用,则会告诉 P 不可调用且不能在“then”分支中接收 ps... 参数。

我想使用 enable_if,但由于我定义了条件的两种情况(T 和 F),我得到了相同函数的重新定义并且也无法编译。

我可以尝试模仿 static_if,但它看起来一点也不优雅。

我想知道是否有一些优雅的方法来检查 P 是否可调用以及 SFINAE 是否可以调用。也许利用我知道 P 的参数类型,(PS...)->std::string.

最佳答案

有两个问题。首先,就像你说的, if 只是一个运行时分支,其次,你不是在检查一个对象是否可调用,而是在检查一个对象是否可以不带参数调用。任何必须带参数的可调用对象都不会通过您的测试。

我会首先实现那个特性。请注意,这对于 C++17 不是必需的:

template<typename, typename = void>
struct is_callable : std::false_type {};

template<typename F, typename... Args>
struct is_callable<F(Args...), void_t<decltype(std::declval<F>()(std::declval<Args>()...))>> : std::true_type {};

然后,你可以使用std::enable_if:

struct Formatter {
    template<typename F, typename... Args>
    auto format(F func, Args&&... args) -> std::enable_if_t<is_callable_v<F(Args...)>> {
        std::cout << func(std::forward<Args>(args)...);
        std::cout << std::endl;
    }

    template<typename T>
    auto format(T&& value) -> std::enable_if_t<!is_callable_v<T()>> {
        std::cout << std::forward<T>(value);
        std::cout << std::endl;
    }
};

您可以像这样实现 void_t:

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

你可以去看看这个实例: Live at coliru

请注意,在 C++17 中,您有 constexpr if 和 std::is_invocable:

struct Formatter {
    template<typename F, typename... Args
    void format(F&& param, Args&&... args) {
        if constexpr (std::is_invocable_v<F, Args...>) {
            std::cout << std::invoke(std::forward<F>(param), std::forward<Args>(args)...);
        } else {
            std::cout << std::forward<F>(param);
            log(std::forward<Args>(args)...);
        }
    }
};

关于c++ - 在 C++14 的 Variadic 模板中选择可调用类型和不可调用类型是否有一个优雅的解决方案,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/43186841/

相关文章:

c++ - 确定可调用对象是否具有模板参数

c++ - 部分专用模板的声明不完整

c++ - 如何使用函数指针定义多重集?

c++ - C++ 新手。关于常量指针的问题

c++ - 如何将 Rcpp::List 转换为 std::vector<double>

c++ - C++按下键

c++ - 与子进程通信的最快方式

添加 g++ -std= 标志时,C++ 打印到终端的速度要慢得多?

c - 为什么我不能将函数指针强制转换为 (void *)?

c - 什么是函数指针