In the below code example, I have a class with a templatized 1-argument constructor, called Acceptor1
. I want to pass in objects of type Foo, Bar, or Baz
to its constructor, chaining the constructor calls. (The reality is that this simplified example reflects something I am trying to do with type erasure: where I want to pass in an object to the eraser class.) What actually happens is that when I try to declare an Acceptor1
object and pass it e.g. a Foo
object, I define a variable that points to a function that accepts a FUNCTION pointer (not an Foo
object) and returns an Acceptor1
. The type of the function pointer is Foo(*)()
. The overall variable is declared of type Acceptor1 (Foo (*)())
.
I can't work around this with any possible enable_if
statement, as shown by the Accessor2
class example, where I didn't even define a 1-argument constructor.
Like I mentioned, the end-use of this is to be able to have a type eraser class that has a constructor looking like Acceptor1
, plus the extra mechanics for type erasure. I've done this and it works fine as long as the type I'm erasing isn't constructed with a 0/1-argument constructor. Or, I can declare and assign the variables in two separate statements. However, I have a lot of them, and this doubles the size of the code, plus takes up a few more CPU cycles, I guess.
Does anyone have any ideas on how to get Acceptor1 test( Foo() )
to be treated as defining an Accessor1
, and initializing it with a Foo
object? It works fine with Accessor1 test( Baz(d, d2) )
, a two-argument situation. It also works fine with literals, e.g. Acceptor1 test( Bar(1.0) )
. Thanks!
Sean
Here's the code:
#include <iostream>
#include <typeinfo>
using namespace std;
struct Foo {
Foo() {
cout << "Foo" << endl;
}
};
struct Bar {
Bar(double a) {
cout << "Bar" << endl;
}
};
struct Baz {
Baz(double a, double b) {
cout << "Baz" << endl;
}
};
struct Acceptor1 {
template<typename T> //no possible enable_if could help this problem
Acceptor1(T f) {
cout << typeid(T).name() << endl;
}
};
struct Acceptor2 {
};
int main()
{
double d, d2;
std::cout << std::endl;
//0 arguments - captures a conversion constructor
Acceptor1 one(Foo()); //F9Acceptor1PF3FoovEE=Acceptor1 (Foo (*)())
cout << "one: " << typeid(one).name() << endl << endl;
//0 arguments - the result we expect
Acceptor1 two = Acceptor1(Foo()); //9Acceptor1=Acceptor1
cout << "two: " << typeid(two).name() << endl << endl;
//There's no possible way to enable_if it out - I deleted the whole constructor
Acceptor2 three(Foo()); //F9Acceptor2PF3FoovEE=Acceptor2 (Foo (*)())
cout << "three: " << typeid(three).name() << endl << endl;
//1 arguments - captures a conversion constructor
Acceptor1 four(Bar(d)); //F9Acceptor13BarE=Acceptor1 (Bar)
cout << "four: " << typeid(four).name() << endl << endl;
//1 arguments - the result we expect
Acceptor1 five = Acceptor1(Bar(d)); //9Acceptor1=Acceptor1
cout << "five: " << typeid(five).name() << endl << endl;
//There's no possible way to enable_if it out - I deleted the whole constructor
Acceptor2 six(Bar(d)); //F9Acceptor23BarE=Acceptor2 (Bar)
cout << "six: " << typeid(six).name() << endl << endl;
//1 arguments - literal
Acceptor1 seven(Bar(5.0)); //9Acceptor1=Acceptor1
cout << "seven: " << typeid(seven).name() << endl << endl;
//2 arguments - the result we expect
Acceptor1 eight(Baz(d, d2)); //9Acceptor1=Acceptor1
cout << "eight: " << typeid(eight).name() << endl << endl;
//2 arguments - the result we expect
Acceptor1 nine = Acceptor1(Baz(d, d2)); //9Acceptor1=Acceptor1
cout << "nine: " << typeid(nine).name() << endl << endl;
using FooMaker = Foo(&)();
using AcceptorFnToBazMaker = Acceptor1(*)(FooMaker); //PF9Acceptor1RF3FoovEE=Acceptor1 (*)(Foo (&)())
cout << "AcceptorFnToBazMaker: " << typeid(AcceptorFnToBazMaker).name() << endl << endl;
return 0;
}
EDIT: It was suggested that this is a duplicate of Default constructor with empty brackets - I agree that they both mention the "most vexing parse" as the source of the problem, but the questions are different. The answer there even includes an example with a function of 2 parameters. In my case, with constructors, only 0/1-argument constructors are treated specially.