简体   繁体   中英

Ternary operator with std::index_sequence

In the following code:

#include <iostream>
#include <utility>
#include <set>

template <typename... Args>
void f(Args... args) {
    std::cout << sizeof...(Args) << " elements.\n";
}

template <std::size_t... Is>
void g (std::index_sequence<Is...>, const std::set<int>& set) {
    f((set.find(Is) == set.end() ? Is : 2*Is)...);
}

int main() {
    g (std::make_index_sequence<10>{}, {1,3,7,8});
}

I want f((set.find(Is) == set.end() ? Is : 2*Is)...); to use Is if set.find(Is) == set.end() but NOTHING otherwise (instead of 2*Is ). Thus the number of arguments passed is not fixed. How to achieve that?

Edit: Sorry, but I simplified the problem too much. The code below reflects more the true problem:

#include <iostream>
#include <utility>
#include <tuple>

template <typename... Args>
void f(Args... args) {
    std::cout << sizeof...(Args) << " elements.\n";
}

struct NullObject {};

template <typename Tuple, std::size_t... Is>
void g (std::index_sequence<Is...>, const Tuple& tuple) {
    f ((std::get<Is>(tuple) != std::get<Is+1>(tuple) ? std::get<Is>(tuple) : NullObject{})...);
}

int main() {
    g (std::make_index_sequence<8>{}, std::make_tuple(2,1.5,'a','a',true,5,5,false));
}

The above does not compile because of mixed types passed by the ternary operator, but I think you can see my idea here. I want to pass NOTHING if the condition std::get<Is>(tuple) != std::get<Is+1>(tuple) , so instead I pass NullObject{} and then somehow try to remove all NullObjects from the arguments of f to get the true arguments passed to f .

You can't use a ternary operator for this - that requires two expressions that have a common type. No dice here. I know of no way of conditionally returning a different type based on a runtime comparison.

You'd have to conditionally forward the type to a different function, by way of just introducing another helper function that builds up the Args... and a simple if statement:

template <size_t... Js>
void fhelper (std::index_sequence<>, const std::set<int>& , std::index_sequence<Js...>) {
    f(Js...);
}

template <std::size_t I, size_t... Is, size_t... Js>
void fhelper (std::index_sequence<I, Is...>, const std::set<int>& set, std::index_sequence<Js...>) {
    if (set.find(I) == set.end()) {
        fhelper(std::index_sequence<Is...>{}, set, std::index_sequence<Js..., I>{});
    }
    else {
        fhelper(std::index_sequence<Is...>{}, set, std::index_sequence<Js...>{});
    }
}

int main() {
    fhelper (std::make_index_sequence<10>{}, {1,3,7,8}, std::index_sequence<>{});
}

I just wanted to share the solution to the general problem that I sought. Made possible by Barry's solution to the specific problem described above:

#include <iostream>
#include <utility>
#include <tuple>

template <template <std::size_t, typename> class Check, typename F, size_t... Js, typename Tuple>
void screenArguments (std::index_sequence<>, std::index_sequence<Js...>, const Tuple& tuple) {
    F()(std::get<Js>(tuple)...);
}

template <template <std::size_t, typename> class Check, typename F, std::size_t I, size_t... Is, size_t... Js, typename Tuple>
void screenArguments (std::index_sequence<I, Is...>, std::index_sequence<Js...>, const Tuple& tuple) {
    if (Check<I, Tuple>::execute(tuple))
        screenArguments<Check, F>(std::index_sequence<Is...>{}, std::index_sequence<Js..., I>{}, tuple);
    else
        screenArguments<Check, F>(std::index_sequence<Is...>{}, std::index_sequence<Js...>{}, tuple);
}

template <template <std::size_t, typename> class Check, typename F, typename Tuple, std::size_t N = std::tuple_size<Tuple>::value>
void passCertainArguments (const Tuple& tuple) {
    screenArguments<Check, F> (std::make_index_sequence<N>{}, std::index_sequence<>{}, tuple);
}

// Testing
#include <typeinfo>

template <typename... Args>
void foo (Args&&...) {
    std::cout << sizeof...(Args) << " elements passed into foo.\n";
}

struct Foo {
    template <typename... Args>
    void operator()(Args&&... args) {
        foo(std::forward<Args>(args)...);
    }
};

template <typename... Args>
void bar (Args&&...) {
    std::cout << sizeof...(Args) << " elements passed into bar.\n";
}

struct Bar {
    template <typename... Args>
    void operator()(Args&&... args) {
        bar(std::forward<Args>(args)...);
    }
};

template <std::size_t N, typename Tuple>
struct CheckArguments {
    static bool execute (const Tuple& tuple) {
        return std::get<N>(tuple) != std::get<N+1>(tuple);
    }
};

struct NullObject {};

template <std::size_t N, typename Tuple>
struct CheckNotNullObject {
    static bool execute (const Tuple& tuple) {
        return typeid(std::get<N>(tuple)) != typeid(NullObject);
    }
};

template <typename F, typename... Args>
void executeWithoutNullObject (Args&&... args) {
    passCertainArguments<CheckNotNullObject, F> (std::forward_as_tuple(args...));
}

template <typename F, typename... Args>
void executeRemoveConsecutiveRepeatElements (Args&&... args) {
    const auto tuple = std::forward_as_tuple(args...);
    passCertainArguments<CheckArguments, F, decltype(tuple), sizeof...(Args) - 1> (tuple);
}

int main() {
    executeWithoutNullObject<Foo> (3, 5, 'a', true, NullObject{}, 'b', 5.8, NullObject{}, NullObject{}, '!', 2);
    // 8 elements passed into foo.  (the 3 NullObjects are removed)
    executeRemoveConsecutiveRepeatElements<Bar> (2, 1.5, 'a', 'a', true, 5, 5, false);
    // 5 elements passed into bar.  (the first 'a', the first 5, and 'false' removed)
}

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM