2013-06-27 4 views
3

Позволяет рассмотривать следующий код:неявное преобразование аргумента конструктора в C++ 11

class A{ 
public: 
    A(int x){} 
}; 

class B{ 
public: 
    B(A a){}; 
}; 


int main() { 
    B b = 5; 
    return 0; 
} 

И при компиляции компилятор жалуется:

/home/test/main.cpp:80: candidate constructor not viable: no known conversion from 'int' to 'A' for 1st argument 

Я не хочу, чтобы создать B(int) конструктор - I хотел бы, чтобы компилятор не подразумевал преобразование этого int в объект A.

EDIT:

Прямая инициализация работает следующим образом:

B b(5); 

Можно ли использовать оператор распайка вместо этого?

+3

Нет, вы не можете сделать это неявно. – jrok

+0

Почему я не могу сделать это неявно? –

+0

Пробовал добавлять оператор присваивания из A, например 'B & operator = (const A & a);' или 'B & operator = (A a);'? – Daemin

ответ

1

Просто чтобы быть ясно:

B b = 5; 

является "копией инициализации" не уступка. (См. http://www.gotw.ca/gotw/036.htm).

В этом случае, вы просите компилятор для выполнения двух неявных определенных пользователем преобразований первого, т.е. int -> A, A -> B перед временным B объект передается в конструктор копирования для B b. Компилятору разрешено удалять временный объект, но семантически вы все еще просите язык сделать два перехода по типам.

Все неявное поведение в языках программирования по своей сути страшно. Ради небольшого синтаксического сахара мы просим C++ «сделать какую-то магию, чтобы она просто работала». Неожиданные преобразования типов могут разрушить хаос в крупных сложных программах. В противном случае каждый раз, когда вы написали новую функцию или новый класс, вам придется беспокоиться обо всех других типах и функциях, которые могут повлиять на нее, при этом побочные эффекты размываются по вашему коду. Вы действительно хотите неявных преобразований от int ->apple ->horse ->horse_power ->aeroplane?

По этой причине, C++ допускает только одну неявного определенного пользователя преобразования:

12.3 [класс преобразований.conv]

1 Тип преобразования объектов класса может быть задан конструкторами и функциями преобразования. Эти преобразования называются пользовательскими преобразованиями и используются для неявных преобразований типов (раздел 4), для инициализации (8.5) и для явных преобразований типов (5.4, 5.2.9).

4 Не более чем одно определяемое пользователем преобразование (конструктор или функция преобразования) неявно применяется к одному значению.

Вы лучше либо с помощью явного приведения или «прямой инициализации» оба из которых делают его ясно компилятор и соавторы именно то, что вы пытаетесь сделать. Либо традиционный, либо новый синтаксис синтаксиса:

B b(5); 
B b{5}; 
B b = {5}; 
0

Используйте прямой инициализации вместо:

B b(5); 
+0

спасибо, он работает, но я бы хотел использовать оператор-ассистент - это как-то возможно? –

+1

* cough * 'B (int)' constructor - sorry;) * cough * – Najzero

+0

@ danilo2 Ваш код * не * с использованием оператора присваивания. Он использует * copy initialisation. * – Angew

0

Вы можете использовать преобразующий конструктор, который constrained на преобразования в A.

class B { 
public: 
    // It doesn't hurt to keep that one 
    B(A a){}; 

    template< 
     typename T 
     , EnableIf<std::is_convertible<T, A>>... 
    > 
    B(T&& value) 
    { 
     // How to use the value 
     A a = std::forward<T>(value); 
    } 
}; 

// Now B b = foo; is valid iff A a = foo; is, except for a few exceptions 

Удостоверьтесь, что вы понимаете цель ограничения EnableIf. Если вы не используете его, вы столкнетесь с грубыми ошибками компиляции или, что еще хуже: никаких ошибок вообще. Вы также должны тщательно рассмотреть вопрос о том, действительно ли делает конвертируемый B из потенциально большого количества типов. Неявные преобразования, как правило, затрудняют понимание программы, и это может быстро перевесить очевидные выгоды, которые они дают.