Функция определения члена-константы 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  
}