A minimal example is:
#include <vector>
class A {
private:
// where the real initialization happens
void constructor_impl(int);
void constructor_impl(const A&);
public:
A(const auto&... args) {
(constructor_impl(args), ...);
}
};
int main() {
A{0, A{1, 2}}; // correct construction
A{""}; // wrong construction: compiles to an infinite recursion
}
I expect the wrong call A{""}
to raise a compile error. But instead the compiler tries to cast ""
to A
and generates an infinite recursion. GCC, Clang and MSVC generates similar results, so it's unlikely to be a compiler bug.
Why does it happen? How can I refactor my code to avoid this issue?
Your templated constructor is effectively a single argument constructor which accepts arguments of any type, therefore any type is implicitly convertible to A
. Therefore when A
is passed any argument which isn't int
or A
the compiler will attempt to call void constructor_impl(const A&);
via an implicit conversion. As you say this leads to infinite recursion.
To avoid this problem you should mark the constructor as explicit
:
explicit A(const auto&... args) {
This will prevent any implicit conversions to A
and give you the compiler error you expect. In general, all single argument constructors should be marked explicit
to avoid surprising implicit conversions.