неправильное преобразование аргументов предпочтительно при вызове функции

StackOverflow https://stackoverflow.com/questions/663724

Вопрос

Пишу программу под 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, так как он будет часто использоваться с литералами, а это потребует большого количества дополнительных операций ввода (плюс очень легко допустить ошибку).

Это было полезно?

Решение

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

Невозможно контролировать предпочтительный порядок преобразований, но вы можете добавить второй конструктор, принимающий константу 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;
};

Другие советы

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

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

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

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

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

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

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

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

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

Или сделать

MyClass( string("blah") );

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

А 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 );
};

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

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

Лицензировано под: CC-BY-SA с атрибуция
Не связан с StackOverflow
scroll top