11

Consider the following little program:

#include <vector>

class A {
  int a;
  int b;

 public:
  explicit A() = default;
  A(int _a, int _b) : a(_a), b(_b) {}

  int f(const A& a) { return 0; }
  int f(std::vector<int> a) { return 1; }
};
int g(const A& a) { return 2; }
int g(std::vector<int> a) {  return 3; }

int main() {
  A a(1,2);
  //   a.f({}); //ambiguous according to gcc
  g({}); //ambiguous according to gcc
  return 0;
}

GCC 10.2 refuse to compile it: it says the call to g({}) and a.f({}) are ambiguous. Clang compile this without complaining.

It seems to me that g(const A&) shouldn't be considered in overload resolution because implicit conversion from no arguments is not allowed: A::A() is marked explicit.

I am not confident that the fault isn't mine and, in any case, I'd like to find a workaround.

Is there another default generated constructor that could be the source of my problem?

You can try it on the compiler explorer.

This SO post was brought to my attention. Its answer tells us which compiler is right: it's GCC. But it does not tell us how to obtain the desired behavior with those overload rules.

AFoley
  • 113
  • 8

1 Answers1

3

You're right that it seems like a bug. The commented out line below fails to compile for the exact reason that there should be no ambiguity.

Got a workaround for you using std::initializer_list:

#include <fmt/core.h>
#include <vector>

class A {
  int a;
  int b;

 public:
  explicit A() = default;
  A(int _a, int _b) : a(_a), b(_b) {fmt::print("Aab\n");}

  void f(const A& a) { fmt::print("A\n"); }
  void f(std::vector<int> a) { fmt::print("vector\n"); }
  void f(std::initializer_list<int> l) {
      return f(std::vector<int>(l));
  }
};
void g(const A& a) { fmt::print("A\n"); }
void g(std::vector<int> a) { fmt::print("vector\n"); }
void g(std::initializer_list<int> a) {return g(std::vector<int>(a)); }

int main() {
  A a(1,2);
  A a2 = A();
  //A a3 = {};
  a.f({});
  g({});
  return 0;
}
Mark H
  • 4,246
  • 3
  • 12