了解转换操作符的C ++选择

时间:2018-08-01 16:05:32

标签: c++ c++11

我有一个类似可选类的类(因为它在C ++ 17中,所以我不能使用可选类)。它包含一个(可能的)值以及一个指示其是否有效的标志。我有一个显式的布尔运算符和一个转换运算符来获取值。问题是,有时C ++会在显式bool上下文(if语句)中选择bool运算符,而其他时候则不会。谁能帮助我了解这种行为:

#include <algorithm>
#include <stdexcept>

template <typename T>
struct maybe {
    maybe()        : valid_(false) {}
    maybe(T value) : valid_(true)  { new (&value_) T(value); }

    operator T&&() {
        return std::move(value());
    }

    explicit operator bool() const {
        return valid_;
    }

    T& value() {
        if (!valid_) {
            throw std::runtime_error("boom");
        }
        return value_;
    }

private:
    union {
        T value_;
    };
    bool valid_;
};


int main() {
    // fine, uses operator bool()
    maybe<std::pair<int,int>> m0;
    if (m0) {
        std::pair<int,int> p = m0;
        (void)p;
    }

    // throws error, uses operator T&&()
    maybe<double> m1;
    if (m1) {
        double p = m1;
        (void)p;
    }
}   

3 个答案:

答案 0 :(得分:4)

只要您写:

<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script>
    <!DOCTYPE html>

    <head>
      <meta http-equiv="author" content="Chris" />
      <script>
        $(function() {
          jQuery('#one').hide();
          jQuery('#fadeToggle').click(function() {
            jQuery('#one').fadeToggle(500);
          });
          jQuery('#fadeTo').click(function() {
            jQuery('#two').fadeTo(500, .5);
          });
          jQuery('#congrats').animate({
              marginLeft: '+=1200px',
            },
            10000,
          );
          jQuery('#congrats').fadeOut(11000);
          jQuery('#opacity').click(function() {
            jQuery(this).addClass("faded");
          });
        });
      </script>
    </head>

    <body>
      <div id="one">
        <p>This is the fadeToggle() method carrying out a "well-defined operation on data," the jQuery object ('#box'), in this case. The parameter is "500," which stands for half a second.</p>
      </div>
      <input type="submit" id="fadeToggle" value="fadeToggle()" />
      <div id="two">
        <p>This is the fadeTo() method.</p>
      </div>
      <input type="submit" id="fadeTo" value="fadeToggle()" />
      <div id="heading">
        <img id="congrats" src="congrats.gif" />
      </div>
      <img id="opacity" src="congrats.gif" />
    </body>

    </html>

那等于写了:

implementation "com.google.firebase:firebase-core:16.0.0"

这称为到if (x) 的上下文转换(请注意,它是直接初始化的,因此bool __flag(x); if (__flag) 转换函数是一个候选方法)。

当我们针对bool对该结构进行重载解析时,只有一个有效的候选者:explicit

但是当我们为m0对该结构进行重载解析时,有两个:explicit operator bool() constm1,因为explicit operator bool() const可转换为operator double&&()。后者是更好的匹配,因为double转换函数具有额外的bool限定,即使我们必须进行额外的const转换也是如此。因此,它赢了。

只需从您的界面中删除bool,因为此类型的意义不大。

答案 1 :(得分:1)

一旦T可转换为booldouble isstd::pair不可用),您的两个运算符就会匹配,并且您会得到一个模棱两可的呼叫,或者一个出于某种原因可能是更好的选择。

您应该只提供两个运算符之一,而不要同时提供。

答案 2 :(得分:1)

您的operator bool和转换运算符在此设计中含糊不清。

  • 在第一个上下文中,std::pair<int,int>不会强制转换为bool 因此使用显式布尔转换运算符。

  • 在第二种情况下,double 确实被强制转换为bool,因此T 使用转换运算符,该运算符返回double,然后 隐式转换为bool

请注意,在第二种情况下,您正在调用std::move,这会使value处于有效但未定义的状态,当您将value投射到double时会导致未定义的行为在if块中第二次。

我将使用命名成员函数来指示其是否有效,并修改转换运算符:

#include <algorithm>
#include <stdexcept>

template <typename T>
struct maybe {
    maybe()        : valid_(false) {}
    maybe(T value) : valid_(true)  { new (&value_) T(value); }

    operator T&&() && { return std::move(value_); } // if rvalue
    operator T&() & { return value_; } // if lvalue
    operator const T&() const & { return value_; } // if const lvalue

    bool valid() const { return valid_; }

    T& value() {
        if (!valid_) {
            throw std::runtime_error("boom");
        }
        return value_;
    }

private:
    union {
        T value_;
    };
    bool valid_;
};

int main() {
    // fine, uses operator bool()
    maybe<std::pair<int,int>> m0;
    if (m0.valid()) {
        std::pair<int,int> p = m0;
        (void)p;
    }

    // throws error, uses operator T&&()
    maybe<double> m1;
    if (m1.valid()) {
        double p = m1;
        (void)p;
    }
}   

编辑:仅当value_对象是右值引用时,转换运算符才应从成员maybe中移出。在这种情况下,在函数签名后专门使用&& -有关更多信息,请参见Kerrek SB's answer