Конструкторы C++ с 0/1 аргументом не связываются должным образом при передаче в конструктор шаблона
В приведенном ниже примере кода у меня есть класс с шаблонным конструктором с одним аргументом, который называется Acceptor1
. Я хочу передать объекты типаFoo, Bar, or Baz
к его конструктору, связывая вызовы конструктора. (На самом деле этот упрощенный пример отражает то, что я пытаюсь сделать со стиранием типа: где я хочу передать объект в класс ластика.) На самом деле происходит следующее: когда я пытаюсь объявитьAcceptor1
объект и передать его, например, Foo
объект, я определяю переменную, которая указывает на функцию, которая принимает указатель FUNCTION (не Foo
объект) и возвращает Acceptor1
. Тип указателя функцииFoo(*)()
. Общая переменная объявляется типаAcceptor1 (Foo (*)())
.
Я не могу обойти это ни с каким возможным enable_if
заявление, как показано Accessor2
пример класса, где я даже не определил конструктор с одним аргументом.
Как я уже упоминал, конечная цель этого - иметь класс ластика типа, конструктор которого выглядит как Acceptor1
, плюс дополнительная механика стирания шрифта. Я сделал это, и он отлично работает, пока тип, который я стираю, не построен с конструктором с 0/1 аргументом. Или я могу объявить и назначить переменные в двух отдельных инструкциях. Тем не менее, у меня их много, и это удваивает размер кода, плюс, я думаю, занимает еще несколько циклов процессора.
Есть ли у кого-нибудь идеи, как получить Acceptor1 test( Foo() )
рассматриваться как определение Accessor1
, и инициализируя его Foo
объект? Он отлично работает сAccessor1 test( Baz(d, d2) )
, ситуация с двумя аргументами. Он также отлично работает с литералами, напримерAcceptor1 test( Bar(1.0) )
. Благодарность!
Шон
Вот код:
#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;
}
РЕДАКТИРОВАТЬ: Было высказано предположение, что это дубликат конструктора по умолчанию с пустыми скобками - я согласен, что они оба упоминают "самый неприятный синтаксический анализ" как источник проблемы, но вопросы разные. Ответ там даже включает пример с функцией 2 параметров. В моем случае с конструкторами специально обрабатываются только конструкторы с 0/1 аргументом.
1 ответ
+ Изменить Acceptor1 test( Foo() )
к Acceptor1 test((Foo()))
или даже лучше было бы Acceptor1 test{Foo()}
или возможно (как и вы) Acceptor1 test = Acceptor1(Foo())
;
Компилятор может интерпретировать строку как действительное объявление функции и инициализацию объекта. В стандарте (насколько я помню) сказано, что в таком случае воспринимайте это как объявление функции. Вот что по этому поводу написано в cppreference:
В случае неоднозначности между объявлением переменной с использованием синтаксиса прямой инициализации (1) (с круглыми скобками) и объявлением функции компилятор всегда выбирает объявление функции. Это правило разрешения неоднозначности иногда противоречит интуиции и называется наиболее неприятным синтаксическим анализом.
И, следовательно, вы это получите.
Чтобы компилятор интерпретировал это как инициализацию объекта, добавьте паратесис ()
вокруг, чтобы сделать его недопустимым объявлением функции. Визуально, все, что вам нужно сделать, это убедиться, что это недопустимый синтаксис объявления функции.