的static_cast<>具有隐式转换运算符的类上的行为,该运算符返回const引用

时间:2016-01-16 16:02:52

标签: c++ c++11

我有以下类(剥离以仅包含相关部分):

#include <string>

class Text
{
private:
    std::string _text;

public:
    Text(std::string&& text) :
        _text(std::move(text))
    {
    }

    operator const std::string&() const
    {
        return _text;
    }
};

我的问题是:如果我想获得const std::string&,我可以这样做而不会受到任何惩罚:

 Text text("fred");

 auto& s = static_cast<std::string>(text);

或者这会构建一个中间std::string,我最终会得到一个引用?这种情况是否有标准方法?我是C ++的新手。

4 个答案:

答案 0 :(得分:10)

不,当您调用static_cast<std::string>(text)时,您正在调用隐式定义的复制构造函数并创建临时对象。

但是,如果你要打电话

auto& s = static_cast<const std::string&>(text);

,然后您将正确地调用显式转换运算符operator const Noisy&()

让我们尝试一下

struct Noisy {
    Noisy() { std::cout << "Default construct" << std::endl; }
    Noisy(const Noisy&) { std::cout << "Copy construct" << std::endl; }
    Noisy(Noisy&&) { std::cout << "Move construct" << std::endl; }
    Noisy& operator=(const Noisy&) { std::cout << "C-assign" << std::endl; return *this; }
    Noisy& operator=(Noisy&&) { std::cout << "M-assign" << std::endl; return *this; }
    ~Noisy() { std::cout << "Destructor" << std::endl; }
};

class Text {
public:
    Text(Noisy&& text) : _text(std::move(text)) {}
    operator const Noisy&() const { return _text; }
private:
    Noisy _text;
};

测试1

int main() {
    Text text(Noisy{});
    const auto& s = static_cast<Noisy>(text); // Needs 'const' to bind to temporary.
}
  

默认构造
  移动构造
  析构函数
  复制构造
  析构函数
  析

测试2

int main() {
    Text text(Noisy{});
    auto& s = static_cast<const Noisy&>(text);
}
  

默认构造
     移动构造
     析构函数
     析

注意:使用选项-fno-elide-constructors进行编译以避免复制省略优化。

答案 1 :(得分:1)

我认为,static_caststd::string而不是const std::string &时,此处的行为取决于C ++ 11标准的这一部分:

[expr.static.cast] [5.2.9.4]:

  

表达式e可以使用T形式的static_cast显式转换为static_cast<T>(e)类型   如果声明T t(e);对于一些发明的临时变量t(8.5),它是格式良好的。这样的效果   显式转换与执行声明和初始化然后使用临时转换相同   变量作为转换的结果。当且仅当初始化时,表达式e用作glvalue   将它用作glvalue。

如果您static_castconst std::string &,那么我希望不会复制。但是,如果您static_caststd::string,则必须获得std::string值 - decltype(static_cast<T>(e))T类型相同。构建新std::string的唯一方法是使用std::string副本ctor,并且因为您的转换运算符未标记为explicit,所以它是一个有效的隐式转换来获取std::string 1}}来自Text那样。

  

或者这会构造一个中间的std :: string,我最终会得到一个引用?

它将构建一个中间std::string,您最终会按获取

答案 2 :(得分:0)

出于这个原因,这甚至都不是合法的C ++。如果你对某些东西进行static_cast,那么你就会明白那个东西而不是其他东西

答案 3 :(得分:0)

首先,编译器在强制转换操作的中间创建一个临时变量:

int main()
{
    string temp = "foo";
    cout << &temp << endl;
    Text text(move(temp));
    auto s = static_cast<string>(text);
    cout << &s << endl;
    return 0;
}

$ main
0x7fffb9dd7530
0x7fffb9dd7540

如您所见,变量的地址已更改

其次,当您使用const, volatile时,编译器会隐藏所有auto等修饰符

void f (int i) {}

auto s = static_cast<const string&>(text);
f(s);
  

错误:无法将'std :: basic_string'转换为'int'作为参数   '1'到'void f(int)'

因此,如果您需要const std::string&,则需要将其应用于auto变量:

const auto& s = static_cast<string>(text);
f(s);
  

错误:无法将'const std :: basic_string'转换为'int'   参数'1'到'void f(int)'

正如您所见,auto关键字隐藏了const标识符。