c++ - 如果编译器不支持,将可变参数传递给 lambda 表达式的解决方法

标签 c++ c++11 lambda

我有两个解决方案,都按我的预期工作,但第一个 仅适用于较新版本的 GCC(肯定是 5.2.1),第二个适用于 GCC >= 4.8.3。问题是,在我的工作中,不幸的是我无法访问更新版本的 GCC。

第一个解决方案基本上是我想要的行为和复杂程度。第二个也有效,但这是我写过的最丑陋的东西,老实说,我不知道这是否是正确的代码(但它有效)。

好的,首先我将描述我面临的问题: 我在这里有一个名为 logic() 的函数,它使用可调用对象来调用其他指令中的一些附加逻辑。这些指令当然不在这里,因为它们不是必需的,它们被标记为(X)

template <typename C>
void logic (C c) {
    // before callable actions (X)
    c ();
    // after callable actions (X)
}

作为可调用对象,我想做的是使用适当的类型设置一些 bool 值 - set_single() 函数。对于示例,我将使用一些额外的类型:

struct P1 {};
struct P2 {};
struct P3 {};

考虑第一个解决方案:

namespace Solution1 {

template <typename T>
void set_single (bool c) {
    // use type T to set c value - details are not the case here.

    std::cout << "value = " << c << std::endl;
    std::cout << "P1 = " << std::is_same<T, P1>::value << std::endl;
    std::cout << "P2 = " << std::is_same<T, P2>::value << std::endl;
    std::cout << "P3 = " << std::is_same<T, P3>::value << std::endl;
}

template <typename T, typename K, typename... Ts, typename... Vs>
void set_single (bool t, bool k, Vs&&... args) {
    set_single<T> (t);
    set_single<K, Ts...> (k, std::forward<Vs> (args)...);
}

template <typename... Ts, typename... Args>
void set (Args&&... args) {
    static_assert (sizeof... (Ts) == sizeof... (Args), "");

    logic ([&] () {
        set_single<Ts...> (std::forward<Args> (args)...);
    });
}

}

set() 函数是一个包装器,它对用户是公开的,set_single() 是实现细节,所以它们是隐藏的(为简单起见,它们没有写在一个类)。

因此,将可调用对象传递给 set() 函数内的 logic() 函数 我调用 set_single() 函数任意次数传递我需要的具有相应类型的所有值。因此,例如,用法可能是这样的:

Solution1::set<P1, P2, P3> (true, false, true);

这将使用类型 P1 设置为真,类型 P2 设置为假,类型 P3 设置为真。

因此,当您的编译器不支持将可变参数传递给 lambda 表达式时,我们有一个解决方案。

namespace Solution2 {

template <typename... Ts>
struct X {
    X () = default;
    X (Ts... t) : tup {std::make_tuple (t...)} {}
    std::tuple<Ts...> tup;
};

template <int...>
struct Ints {};

template <int N, int... Is>
struct Int_seq : Int_seq<N-1, N, Is...> {};

template <int... Is>
struct Int_seq<0, Is...> {
    using type = Ints<0, Is...>;
};

template <int... Is>
using Iseq = typename Int_seq<Is...>::type;

template <int I, typename... Args, typename... Types>
void set_single (Ints<I>, const std::tuple<Args...>& a, const std::tuple<Types...>& t) {
    std::cout << "value = " << std::get<I> (a) << std::endl;
    auto p1 = std::get<I> (t);
    auto p2 = std::get<I> (t);
    auto p3 = std::get<I> (t);
    std::cout << "P1 = " << std::is_same<P1, decltype (p1)>::value << std::endl;
    std::cout << "P2 = " << std::is_same<P2, decltype (p2)>::value << std::endl;
    std::cout << "P3 = " << std::is_same<P3, decltype (p3)>::value << std::endl;
}

template <int I, int K, int... Is, typename... Args, typename... Types>
void set_single (Ints<I, K, Is...>, const std::tuple<Args...>& a, const std::tuple<Types...>& t) {
    set_single (Ints<I> {}, a, t);
    set_single (Ints<K, Is...> {}, a, t);
}

template <typename... Ts, typename... Args>
void set (Args... args) {
    static_assert (sizeof... (Ts) == sizeof... (Args), "");

    X<Ts...> types {};
    X<Args...> arguments {args...};

    logic ([&types, &arguments] () {
        set_single (Iseq<std::tuple_size<decltype (arguments.tup)>::value-1> {}, arguments.tup, types.tup);
    });

}

}

我使用 Solution2::X 类型来存储值和类型。我试着做一些 std::bind() 的东西,但是很痛苦,所以我用了 一些整数序列(可能实现很差)。就像我说的,这两种解决方案都有效,但第二种方案不如第一种方案那么酷。

你们中的任何人都可以告诉我 Solution2 是否可以用某种更简单的解决方案代替吗?我很确定其中存在一些。

输出的用法如下:

Solution1::set<P1, P2, P3> (true, false, true);

value = 1
P1 = 1
P2 = 0
P3 = 0
value = 0
P1 = 0
P2 = 1
P3 = 0
value = 1
P1 = 0
P2 = 0
P3 = 1


Solution2::set<P1, P2, P3> (true, false, true);

value = 1
P1 = 1
P2 = 0
P3 = 0
value = 0
P1 = 0
P2 = 1
P3 = 0
value = 1
P1 = 0
P2 = 0
P3 = 1

最佳答案

您需要使用一些标记来保留给定的类型,因为它们可能不是默认可构造的。

我会这样实现:

#if 1 // Not in C++11 // make_index_sequence
#include <cstdint>

template <std::size_t...> struct index_sequence {};

template <std::size_t N, std::size_t... Is>
struct make_index_sequence : make_index_sequence<N - 1, N - 1, Is...> {};

template <std::size_t... Is>
struct make_index_sequence<0u, Is...> : index_sequence<Is...> {};

#endif // make_index_sequence

#if 1 // Not in C++11 // tuple_element_t
template <std::size_t I, typename T>
using tuple_element_t = typename std::tuple_element<I, T>::type;
#endif

namespace Solution3 {

    template <typename T>
    struct bool_impl {
        using type = bool;
    };

    template <typename T>
    using bool_t = typename bool_impl<T>::type; // or simply using bool_t = bool

    template <typename T>
    void set_single (bool c) {
        // use type T to set c value - details are not the case here.

        std::cout << "value = " << c << std::endl;
        std::cout << "P1 = " << std::is_same<T, P1>::value << std::endl;
        std::cout << "P2 = " << std::is_same<T, P2>::value << std::endl;
        std::cout << "P3 = " << std::is_same<T, P3>::value << std::endl;
    }

    template <typename T> struct Tag {};

    template <typename T1, typename T2, std::size_t ... Is>
    void set_singles (Tag<T1>, T2 t, index_sequence<Is...>) {
        int dummy[] = {0, (set_single<tuple_element_t<Is, T1>>(std::get<Is>(t)), 0)...};
        static_cast<void>(dummy); // Avoid warning for unused variable
    }

    template <typename... Ts>
    void set (bool_t<Ts>... args) {
        Tag<std::tuple<Ts...>> tag;
        auto bools = std::make_tuple(args...);
        auto seq = make_index_sequence<sizeof...(Ts)>();

        logic ([&]() {
            set_singles(tag, bools, seq);
        });
    }

}

Demo

以及针对最新编译器的简化版本

Demo

关于c++ - 如果编译器不支持,将可变参数传递给 lambda 表达式的解决方法,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/39257625/

相关文章:

c++ - std::remove_if - lambda,不从集合中删除任何内容

c# - 如何使用 Microsoft Shims 检测方法调用是 DAMP

c++ - 如何调用传递定义为 protected 类的对象的方法

java - 来自 Java 的 Qt Android : How to call Toast. makeText?

c++ - 在 C++ 标准的哪些版本中, "(i+=10)+=10"具有未定义的行为?

c++ - 为什么要向这个宏传递三个参数?

c++ - std::function 是如何实现的?

c# - 将 Expression<Func<T,object>> 转换为字符串

c++ - 从 16 位 PCM 中去除 C++ 中的音频噪声(嘶嘶声)

c++ - 深拷贝构造函数数组类C++