Функция определения члена-константы const-reference
Пример наличия ссылочной функции-члена выглядит примерно так:
#include <stdio.h>
#include <stdexcept>
#include <string>
// Easy access to literals
using namespace std::literals;
// File wrapper
class File {
private:
// The wrapped file
FILE *_file;
public:
File(const char *name) :
_file(fopen(name, "r")) {
// unable to open the file?
if (!_file) throw std::runtime_error{ "Unable to open file: "s + name };
}
~File() {
fclose(_file);
}
// Convert to the underlying wrapped file
operator FILE *() & {
return _file;
}
// TODO: Member functions for working with the file
};
Это хорошо работает. Невозможно напрямую получить базовый указатель FILE из неназванного временного. Однако, если мы сделаем оператор литья также const-квалифицированным, это больше не работает.
Различные компиляторы просто проглатывают его без жалобы, даже если это ужасно полезная идея. Возьмем, например, функцию std::string:: c_str(). Вы считаете, что он должен быть квалифицированным по ссылке (потому что в противном случае у вас есть недопустимый указатель), но это не так.
Является ли это дырой в стандарте С++ 11? Я что-то пропустил?
Ответы
Ответ 1
Временной может быть привязан к const&
квалифицированному объекту, и ref-qualifier эффективно квалифицирует неявно переданный объект (*this
). Если вы хотите запретить вызовы во временном режиме, но допускаете lvalues, вы можете = delete
перенаправить ссылку на rvalue и реализовать версию lvalue. Использование const
квалифицированных эталонных квалификаторов для обоих операторов требует только одной реализованной и одной реализации = delete
d:
class File {
// ...
FILE* _file;
public:
operator FILE*() const&& = delete;
operator FILE*() const& { return this->_file; }
// ...
};
Сетевой эффект заключается в том, что вы можете использовать преобразование только для объектов, к которым вы используете lvalue:
int main() {
File f;
File const cf{};
FILE* fp = f; // OK
FILE* cfp = cf; // OK
FILE* tfp = File(); // ERROR: conversion is deleted
FILE* mfp = std::move(cf); // ERROR: conversion is deleted
}