неправильное преобразование аргумента предпочтительнее при вызове функции - PullRequest
3 голосов
/ 19 марта 2009

Я пишу программу под MS Visual C ++ 6.0 (да, я знаю, что она древняя, нет, я ничего не могу сделать, чтобы обновить). Я вижу какое-то поведение, которое мне кажется странным. У меня есть класс с двумя конструкторами, определенными так:

class MyClass
{
public:
    explicit MyClass(bool bAbsolute = true, bool bLocation = false) : m_bAbsolute(bAbsolute), m_bLocation(bLocation) { ; }
    MyClass(const RWCString& strPath, bool bLocation = false);

private:
    bool m_bAbsolute;
    bool m_bLocation;
};

Когда я создаю экземпляр этого класса с таким синтаксисом: MyClass("blah"), он вызывает первый конструктор. Как вы можете видеть, я добавил к нему ключевое слово explicit в надежде, что он этого не сделает ... без игры в кости. Похоже, что он предпочел бы преобразование из const char * в bool вместо преобразования в RWCString, у которого есть конструктор копирования, который принимает const char *. Почему он это делает? Я бы предположил, что, учитывая два возможных варианта, таких как этот, он сказал бы, что это неоднозначно. Что я могу сделать, чтобы предотвратить это? Если это вообще возможно, я бы хотел избежать явного приведения аргумента strPath к RWCString, так как он будет часто использоваться с литералами, и это очень много дополнительной типизации (плюс действительно простая ошибка сделать).

Ответы [ 7 ]

9 голосов
/ 19 марта 2009

Явное здесь не поможет, так как конструктор не является частью неявного преобразования, просто получатель.

Нет способа контролировать предпочтительный порядок преобразований, но вы можете добавить второй конструктор, который принимает const char *. Например:

class MyClass
{
public:
    MyClass(bool bAbsolute = true, bool bLocation = false);
    MyClass(const RWCString& strPath, bool bLocation = false);
    MyClass(const char* strPath, bool bLocation = false);

private:
    bool m_bAbsolute;
    bool m_bLocation;
};
6 голосов
/ 19 марта 2009

Эндрю Грант предоставил решение. Я хочу сказать вам , почему не работает так, как вы пытались. Если у вас есть две жизнеспособные функции для аргумента, то вызывается та, которая лучше всего соответствует аргументу. Второй требует пользовательского преобразования, в то время как первый требует только стандартного преобразования. Вот почему компилятор предпочитает первое над вторым.

1 голос
/ 19 марта 2009

Если вы не хотите продолжать его разыгрывать, то мне кажется, что вам, возможно, придется сделать еще один ctor, который принимает постоянный символ *.

Это то, что я, вероятно, сделал бы в этой ситуации.

(Не уверен, почему вы делаете ctor с типом, который вы не передаете для большей части его использования.)

редактирование:

Я вижу, что кто-то уже опубликовал это, пока я печатал свой

0 голосов
/ 19 марта 2009

Ключевое слово explicit сообщает компилятору, что оно не может неявно преобразовать значение типа аргумента в объект вашего класса, как в

struct C { explicit C( int i ): m_i(i) {}; int m_i; };
C c = 10;//disallowed
C c( 2.5 ); // allowed

C ++ имеет набор правил, чтобы определить, какой конструктор должен вызываться в вашем случае - я не знаю из глубины души, но вы можете интуитивно увидеть, что аргументы по умолчанию приводят к неоднозначности.

Вы должны продумать эти значения по умолчанию.

Вы можете использовать некоторые статические именованные методы построения. Или вы можете использовать другой класс (что не является плохим выбором с точки зрения дизайна). В любом случае вы позволяете клиентскому коду решать, какой конструктор использовать.

struct C {
  static C fromString( const char* s, const bool b = true );
  static C fromBools( const bool abs = true, const bool b = true );
};

или

struct CBase {
    bool absolute; 
    bool location;
    CBase( bool abs=true, bool loc=true );
};

struct CBaseFromPath {
    // makes 'absolute' true if path is absolute
    CBaseFromPath( const char* s, const bool loc );
};
0 голосов
/ 19 марта 2009

Вы можете добавить конструктор, который явно принимает const char *

MyClass(const char* strPath, bool bLocation = false); // Thanks Andrew Grant!

Или сделать

MyClass( string("blah") );

Компилятор по сути знает, как превратить const char * в bool. Нужно было бы посмотреть, есть ли для любого из типов первого аргумента конструкторов MyClass конструктор, который возьмет заданный вами тип источника или он может привести его к типу, который принимается любой из конструкторов любого из типов первого аргумента ваших конструкторов MyClass, или ... ну, вы видите, куда это идет, и это только для первого аргумента. Так лежит безумие.

0 голосов
/ 19 марта 2009

Вы уверены , что он действительно вызывает первый конструктор? Вы называете это жестко закодированной строкой или она скрыта за #define? Вы уверены, что #define - это то, что вы думаете? (Попробуйте скомпилировать с параметром / Ef, чтобы получить расширенный вывод препроцессора, и посмотрите, будет ли вызов выглядеть так, как вы ожидаете.)

РЕДАКТИРОВАТЬ: Исходя из этого и других комментариев, я предлагаю добавить еще один конструктор для const char*. Это возможно?

0 голосов
/ 19 марта 2009

Не знаете, почему следует путать ссылку на строку и bool? Я видел проблемы с bool и int.
Можете ли вы потерять значение по умолчанию для первого конструктора - возможно, так как это делает его конструктором по умолчанию для MyClass (), то это также значение по умолчанию, если оно не может соответствовать аргументам

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...