lundi 28 septembre 2015

Cannot remove unwanted overloads

The function transform conducted by

const std::vector<int>         a = {1,     2,       3,       4,    5};
const std::vector<double>      b = {1.2,   4.5,     0.6};
const std::vector<std::string> c = {"hi", "howdy", "hello", "bye"};
std::vector<double> result(5);

transform<Foo> (result.begin(),
    a.begin(), a.end(),
    b.begin(), b.end(),
    c.begin(), c.end());

is to carry out a generalization of std::transform on multiple containers, outputing the results in the vector result. One function with signature (int, double, const std::string&) would apparently be needed to handle the three containers in this example. But because the containers have different lengths, we instead need to use some overloads. I will test this using these member overloads of a holder class Foo:

static int execute (int i, double d, const std::string& s) {return i + d + s.length();}
static int execute (int i, const std::string& s) {return 2 * i + s.length();}
static int execute (int i) {return 3 * i - 1;}

However, the program will not compile unless I define three other overloads that are never even called, namely with arguments (int, double), (const std::string&) and (). I want to remove these overloads, but the program won't let me. You can imagine the problem this would cause if we had more than 3 containers (of different lengths), forcing overloads with many permutations of arguments to be defined when they are not even being used.

Here is my working program that will apparently show why these extraneous overloads are needed. I don't see how or why the are forced to be defined, and want to remove them. Why must they be there, and how to remove the need for them?

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

bool allTrue (bool a) {return a;}

template <typename... B>
bool allTrue (bool a, B... b) {return a && allTrue(b...);}

template <typename F, size_t... Js, typename Tuple>
typename F::return_type screenArguments (std::index_sequence<>, std::index_sequence<Js...>, Tuple& tuple) {
    return F::execute (*std::get<Js>(tuple)++...);
}

// Thanks to Barry for coming up with screenArguments.
template <typename F, std::size_t I, size_t... Is, size_t... Js, typename Tuple>
typename F::return_type screenArguments (std::index_sequence<I, Is...>, std::index_sequence<Js...>, Tuple& tuple) {
    if (std::get<2*I>(tuple) != std::get<2*I+1>(tuple))
        return screenArguments<F> (std::index_sequence<Is...>{}, std::index_sequence<Js..., 2*I>{}, tuple);
    else
        return screenArguments<F> (std::index_sequence<Is...>{}, std::index_sequence<Js...>{}, tuple);
}

template <typename F, typename Tuple>
typename F::return_type passCertainArguments (Tuple& tuple) {
    return screenArguments<F> (std::make_index_sequence<std::tuple_size<Tuple>::value / 2>{},
        std::index_sequence<>{}, tuple);
}

template <typename F, typename OutputIterator, std::size_t... Is, typename... InputIterators>
OutputIterator transformHelper (OutputIterator result, const std::index_sequence<Is...>&, InputIterators... iterators) {
    auto tuple = std::make_tuple(iterators...);
    while (!allTrue(std::get<2*Is>(tuple) == std::get<2*Is + 1>(tuple)...))
        *result++ = passCertainArguments<F>(tuple);
    return result;
}

template <typename F, typename OutputIterator, typename... InputIterators>
OutputIterator transform (OutputIterator result, InputIterators... iterators) {
    return transformHelper<F> (result, std::make_index_sequence<sizeof...(InputIterators) / 2>{}, iterators...);
}

// Testing
#include <vector>

struct Foo {
    using return_type = int;
    static int execute (int i, double d, const std::string& s) {return i + d + s.length();}
    static int execute (int i, const std::string& s) {return 2 * i + s.length();}
    static int execute (int i) {return 3 * i - 1;}
    // These overloads are never called, but apparently must still be defined.  
    static int execute () {std::cout << "Oveload4 called.\n";  return 0;}
    static int execute (int i, double d) {std::cout << "Oveload5 called.\n";  return i + d;}
    static int execute (const std::string& s) {std::cout << "Oveload6 called.\n";  return s.length();}
};

int main() {
    const std::vector<int>         a = {1,     2,       3,       4,    5};
    const std::vector<double>      b = {1.2,   4.5,     0.6};
    const std::vector<std::string> c = {"hi", "howdy", "hello", "bye"};
    std::vector<double> result(5);

    transform<Foo> (result.begin(),
        a.begin(), a.end(),
        b.begin(), b.end(),
        c.begin(), c.end());
    for (double x : result) std::cout << x << ' ';  std::cout << '\n';
    // 4 11 8 11 14 (correct output)
}

Aucun commentaire:

Enregistrer un commentaire