3

Here I want to simplify my c++ code from version 2 to version 1. In a language like Python, it's simple to refer to a different types of variables like version 1. How can I achieve similar code in c++?

#include <iostream>
#include <vector>

/* version 1: compile failed */
void display(std::vector<int> vi, std::vector<double> vd) {
  // error: operands to ?: have different types
  // ‘std::vector<double>’ and ‘std::vector<int>’
  auto& v = vi.empty() ? vd : vi;
  for (const auto &e : v) {
    std::cout << e << " ";
  }
}

/* version 2 */
void display(std::vector<int> vi, std::vector<double> vd) {
  if (!vi.empty()) {
    for (const auto &e : vi) {
      std::cout << e << " ";
    }
  } else {
    for (const auto &e : vd) {
      std::cout << e << " ";
    }
  }
}



int main(int argc, char *argv[]) {
  std::vector<int> vi{0, 1, 2};
  std::vector<double> vd{10., 11, 12};

  // one of vi, vd can be empty after some modifications
  ...

  display(vi, vd);
  return 0;
}

Supplement:

Here the print function is only an example. My main purpose is to create a reference to std::vector<int> or std::vector<double> according to whether they are empty or not. The following example may

#include <iostream>
#include <vector>

void process_1(std::vector<int> v) {
  for (const auto &e : v) {
    std::cout << e << " ";
  }
  std::cout << std::endl;
}

void process_1(std::vector<double> v) {
  double sum = 0;
  for (const auto &e : v) {
    sum += e;
  }
  std::cout << sum << std::endl;
}

void process_2(std::vector<int>) {
  //...
}

void process_2(std::vector<double>) {
  //...
}

/* Version 2 */
int version_2(int argc, char *argv[]) {
  std::vector<int> vi{0, 1, 2};
  std::vector<double> vd{10., 11, 12};

  if (!vi.empty()) {
    process_1(vi);
  } else {
    process_1(vd);
  }

  // do something

  if (!vi.empty()) {
    process_2(vi);
  } else {
    process_2(vd);
  }

  return 0;
}

/* Version 1 */
int version_1(int argc, char *argv[]) {
  std::vector<int> vi{0, 1, 2};
  std::vector<double> vd{10., 11, 12};

  // in Python, it's easy to create a reference dynamically, eg:
  // ref = vd is len(vi) == 0 else vi
  auto &ref = vd if vi.empty() else vi;

  process_1(ref);
  // do something
  process_2(ref);

  return 0;
}
Beanocean
  • 133
  • 10
  • A side note: you can pass `vi`, `vd` by `const &` (instead of by value) for efficiency. – wohlstad Jul 27 '22 at 13:50
  • I don't think the duplicate fits. It doesn't provide the workaround OP asked for. – HolyBlackCat Jul 28 '22 at 04:38
  • @HolyBlackCat, Could you help to explain the mechanism behind it? Is it because the left-hand side of the equation must be a definite type or can be deducted from the right-hand side? – Beanocean Jul 29 '22 at 02:45

2 Answers2

5

Something along these lines, perhaps:

void display(const std::vector<int>& vi, const std::vector<double>& vd) {
  auto print_sequence = [](const auto& v) {
    for (const auto &e : v) {
      std::cout << e << " ";
    }
  }
  vi.empty() ? print_sequence(vd) : print_sequence(vi);
}
HolyBlackCat
  • 78,603
  • 9
  • 131
  • 207
Igor Tandetnik
  • 50,461
  • 4
  • 56
  • 85
3

What you're searching for is a visitor.

First version:

void display(std::vector<int> vi, std::vector<double> vd) {
    auto vis = [](const auto& v) {
        for (const auto& e : v) {
            std::cout << e << " ";
        }
    };
    if (vi.empty()) {
        vis(vd);
    } else {
        vis(vi);
    }
}

Note however, you don't necessarily need to pass two arguments, you might use a std::variant<std::vector<int>, std::vector<double>>:

void display(std::variant<std::vector<int>, std::vector< double>> vv) {
    auto vis = [](const auto& v) {
        for (const auto& e : v) {
            std::cout << e << " ";
        }
    };
    std::visit(vis, vv);
}

As a variant is constructible from any of the types, you can simply pass it either vi or vd.

This works with arbitrary number of types. Also, suggest passing arguments by const reference (e.g., const std::variant<std::vector< int>, std::vector< double>>&).

lorro
  • 10,687
  • 23
  • 36
  • Did you perhaps mean `std::variant, std::vector>`? As written, the `variant`-using version doesn't make sense - it attempts to iterate over a single value of a primitive type. – Igor Tandetnik Jul 27 '22 at 14:16