Search code examples
c++templatesc++11variadic

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.


Solution

  • 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<>{});
    }