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

时间:2017-04-03 14:07:52

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

我正在为一个类实现简单的文本格式化程序。其中的主要功能可以接收将被连接的值列表。或者,对于参数不是ostream的朋友的情况,我接受转换函数作为第一个参数,将所有其他参数转换为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是可调用的,则无法将其输出到&#34;否则&#34;分支,如果P不可调用,它将告诉P不可调用,并且不能在&#34;然后&#34;中接收ps ...参数。分支。

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

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

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

2 个答案:

答案 0 :(得分:4)

有两个问题。首先,就像你说的那样,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)...);
        }
    }
};

答案 1 :(得分:1)

您可以使用std::enable_if

struct formater{
    template<typename P, typename... PS>
    std::enable_if<std::is_convertible<PARAM, std::function<void(PS...)>>::value, void>::type
    format(const PARAM& p, const PS&... ps){
        cout << p(ps...) << endl;

    }

    template<typename P, typename... PS>
    std::enable_if<!std::is_convertible<PARAM, std::function<void(PS...)>>::value, void>::type
    format(const PARAM& p, const PS&... ps){
        cout << p;
        log(ps...);

    }
};