【问题标题】:wrong argument conversion preferred when calling function调用函数时首选错误的参数转换
【发布时间】:2010-10-14 09:43:20
【问题描述】:

我正在 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,因为它将大量与文字一起使用,而且需要大量额外的输入(加上一个非常容易错误制作)。

【问题讨论】:

    标签: c++ constructor arguments visual-c++-6


    【解决方案1】:

    不知道为什么它应该混淆对字符串和布尔值的引用?我已经看到了 bool 和 int 的问题。
    你能失去第一个构造函数的默认值吗?可能是因为这使它成为 MyClass() 的默认构造函数,那么如果它不能匹配 args 也是默认值

    【讨论】:

    • C++ 标准允许将任何指针转换为布尔值 IIRC,这就是为什么首先允许使用 const char * -> bool 的原因。我只是不知道为什么它似乎更喜欢那个。删除默认 arg 并不理想,可能不会有什么不同,但我会试一试。
    【解决方案2】:

    你确定它真的在调用第一个构造函数吗?你是用硬编码的字符串调用它,还是隐藏在#define 后面?您确定#define 是您认为的那样吗? (尝试使用 /Ef 选项进行编译以获取扩展的预处理器输出,并查看调用是否与您期望的一样。)

    编辑:基于这个和其他 cmets,我的建议是为 const char*. 添加另一个构造函数这可行吗?

    【讨论】:

    • 我用一个裸字符串文字来调用它,没有#define,没有在其他地方定义的常量。而且我几乎可以肯定它正在调用第一个,因为这是当我在调试器中单步执行时它跳转到的地方:)
    【解决方案3】:

    显式在这里没有帮助,因为构造函数不是隐式转换的一部分,只是接收者。

    无法控制转换的首选顺序,但您可以添加第二个构造函数,该构造函数采用 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;
    };
    

    【讨论】:

      【解决方案4】:

      如果您不想继续投射它,那么在我看来,您可能必须制作另一个接受 const char* 的 ctor。

      这就是我在这种情况下可能会做的事情。

      (不知道为什么要使用在大多数情况下都没有传递的类型来制作 ctor。)

      编辑:

      我在打字时看到其他人已经发布了这个

      【讨论】:

      • 我希望 RWCString 可以同时处理文字字符串和 RWCString 的使用。它实际上主要与 RWCString 一起使用;文字只带有一些硬编码(不是我的选择)配置数据。
      【解决方案5】:

      您的选择是添加一个显式接受 const char * 的构造函数

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

      或者做

      MyClass( string("blah") );
      

      编译器本质上知道如何将 const char * 变成 bool。它必须去看看,对于 MyClass 构造函数的任何第一个参数类型,是否有一个构造函数将采用您给它的源类型,或者它是否可以将其转换为接受的类型您的 MyClass 构造函数的任何第一个参数类型的任何构造函数,或者......好吧,您会看到这是怎么回事,并且仅适用于第一个参数。那就是疯狂。

      【讨论】:

        【解决方案6】:

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

        【讨论】:

          【解决方案7】:

          Andrew Grant 提供了解决方案。我想告诉你为什么它不像你尝试的那样工作。如果一个参数有两个可行的函数,则调用与参数最匹配的一个。第二种需要用户定义的转换,而第一种只需要标准的转换。这就是为什么编译器更喜欢第一个而不是第二个。

          【讨论】:

            猜你喜欢
            • 2014-05-31
            • 1970-01-01
            • 2015-05-14
            • 1970-01-01
            • 1970-01-01
            • 2010-09-26
            • 1970-01-01
            • 2011-11-30
            • 1970-01-01
            相关资源
            最近更新 更多