2010-11-03 2 views
13

В следующей строке кода:Переходя неконстантная ссылки на rvalues ​​в C++

bootrec_reset(File(path, size, off), blksize); 

Вызов функции с прототипом:

static void bootrec_reset(File &file, ssize_t blksize); 

Я получаю эту ошибку:

libcpfs/mkfs.cc:99:53: error: invalid initialization of non-const reference of type 'File&' from an rvalue of type 'File'

libcpfs/mkfs.cc:30:13: error: in passing argument 1 of 'void bootrec_reset(File&, ssize_t)'

Я знаю, что вы не можете передавать неконстантные ссылки (const &) на rvalues ​​в соответствии со стандартом. Однако MSVC позволяет это сделать (см. this question). This question пытается объяснить, почему, но ответ не имеет смысла, поскольку он использует ссылки на литералы, которые являются угловыми и, очевидно, должны быть запрещены.

В данном примере видно, что следующий порядок событий будет происходить (как это происходит в MSVC):

  1. File «s конструктор будет вызван.
  2. Ссылка на File и blksize, помещается в стек.
  3. bootrec_reset использует file.
  4. После восстановления с bootrec_reset, временный File уничтожен.

Необходимо указать, что ссылка File должна быть неконстантной, поскольку она является временным дескриптором файла, на который вызывается неконтентные методы. Кроме того, я не хочу передавать аргументы конструктора File на bootrec_reset, который должен быть построен там, и я не вижу причин вручную создавать и уничтожать объект File в вызывающем.

Так что мои вопросы:

  1. Что оправдывает стандарт C++, не о запрете на константные ссылки таким образом?
  2. Как я могу заставить GCC разрешить этот код?
  3. Имеет ли новый стандарт C++ 0x это в любом случае, или есть что-то, что новый стандарт дает мне, что более уместно здесь, например, все это смехотворное о ссылках на rvalue?

ответ

7

Да, тот факт, что простые функции не могут связывать неконстантные ссылки на временные - , но методы могут - всегда меня било. TTBOMK обоснование выходит что-то вроде этого (получены из this comp.lang.c++.moderated thread):

Предположим, у вас есть:

void inc(long &x) { ++x; } 

void test() { 
    int y = 0; 
    inc(y); 
    std::cout << y; 
} 

Если вы разрешили параметр inc()long &x для привязки к временной long копии сделанной из y, этот код, очевидно, не будет делать то, что вы ожидаете - компилятор просто молчал бы код, который оставил бы y без изменений. По-видимому, это был общий источник ошибок в ранние C++ дни.

Если бы я спроектировал C++, я бы предпочел бы, чтобы ссылки, не связанные со связями, привязывались к временным, но запрещали автоматические преобразования от lvalues ​​к временным при привязке к ссылкам. Но кто знает, что, возможно, открыл бы другую банку червей ...

+0

Я не знал, что lvalues ​​были преобразованы во временные числа при привязке к ссылкам. Если это решение имеет смысл. * edit *: Я только что протестировал его, а они нет? –

+0

@Matt: правила изменены с помощью C++ 0x. Это изменение правила затрагивает, например, передавая 'std :: auto_ptr' в качестве аргумента. Во всяком случае, при привязке к ссылке на const, а фактический аргумент не относится к требуемому типу (или производному классу), тогда необходимо обязательно ввести временное. Cheers, –

+0

@Matt: Что вы точно проверили? Мой фрагмент не должен компилироваться, потому что C++ явно запрещает привязывать non-const ref к временному (последние MSVC++ и g ++ замечают это); изменение ref в const ref, конечно же, приведет к сбою компиляции из-за '++ x;'. –

2
  1. Есть довольно спорное решение - неконстантные ссылки на временных допускается, если временный является предметом вызова метода, например, (например, «своп трюк», чтобы освободить память, выделенную вектором , std::vector<type>().swap(some_vector);)
  2. Короче говоря временное название, я не думаю, что вы можете.
  3. Насколько я знаю, это правило существует и в C++ 0x (для регулярных ссылок), но ссылки rvalue специально существуют, поэтому вы можете привязывать ссылки на временные разделы - поэтому изменение bootrec_reset для принятия File && должно сделать код законным ,
+0

Сравнение с случаем доступа к членству на самом деле не так уж и хорошо. Оператор доступа к члену не рассматривает преобразования. Связывание ссылок на const. –

+1

Это ортогональная проблема. Это комбинация обоих (привязка к временным, которые были получены из неявного преобразования), что действительно вызывает неожиданное/неожиданное поведение; вы могли бы позволить ссылаться на временные, но не допускать каких-либо неявных преобразований в этом контексте. –

+0

Но неявные преобразования разрешены при связывании неконстантных ссылок. Ты серьезно не предлагаешь это убрать, не так ли? Затем мы вернемся к тому, что привязаны к ссылкам const, а не к неконстантным ссылкам, где и сейчас язык. –

1

Обратите внимание, что вызов C++ 0x «jibberish» не дает очень благоприятной картины вашей способности кодирования или желания понять язык.

1) На самом деле это не так произвольно. Разрешение неконстантных ссылок на привязку к r-значениям приводит к чрезвычайно запутанному коду. Недавно я подал ошибку в отношении MSVC, которая относится к этому, где нестандартное поведение вызвало несовместимость стандартного кода с компиляцией и/или компиляцией с отклоняющимся поведением.

В вашем случае необходимо учитывать следующее:

#include <iostream> 

template<typename T> 
void func(T& t) 
{ 
    int& r = t; 
    ++r; 
} 

int main(void) 
{ 
    int i = 4; 
    long n = 5; 
    const int& r = n; 

    const int ci = 6; 
    const long cn = 7; 

    //int& r1 = ci; 
    //int& r2 = cn; 

    func(i); 
    //func(n); 

    std::cout << r << std::endl; 
} 

Какой из комментируемых линий вы хотите скомпилировать? Вы хотите, чтобы func(i) изменил свой аргумент и func(n), чтобы НЕ делать это?

2) Вы не можете скомпилировать этот код. Вы не хотите иметь этот код. Будущая версия MSVC, вероятно, собирается удалить нестандартное расширение и не сможет скомпилировать этот код. Вместо этого используйте локальную переменную. Вы всегда можете использовать дополнительную пару фигурных скобок для управления временем жизни этой локальной переменной и заставить ее уничтожаться до следующей строки кода, как и временная. Или ссылки на r-значение.

{ 
    File ftemp(path, size, off); 
    bootrec_reset(ftemp, blksize); 
} 

3) Да, вы можете использовать ссылки на значения C++ 0x r в этом сценарии.

+0

Как я вижу, ваш первый фрагмент не несет ответственности за вопрос о том, следует ли разрешать неконстантные ссылки на временные: Я бы сказал, что этот фрагмент не должен компилироваться исключительно потому, что (a) не должно быть автоматического преобразования из объекта const в неконстантное temp rvalue и (b) ему должно быть запрещено принимать не-const ref для объекта const , Не знаю, действительно ли (а) на C++ уже, но (б) есть. –

+0

@j_random_hacker: Я просто работал над более сложным примером. Но я не понимаю, что ваши (a), r-значения не имеют постоянной. Вы хотите запретить любые автоматические преобразования из объектов const, период? –

+0

@Ben: Хорошо мое понимание немного шаткое, но я думаю, что rvalues ​​могут иметь (или не иметь) constness - хотя разница имеет значение только для объектов типа класса (если rvalue является const, то могут быть вызваны только методы const Это). И да, я предлагаю, чтобы автоматическое преобразование *, которое создавало бы временный * из объекта const, должно быть разрешено - вы видите непредвиденные последствия, вытекающие из этого, возможно? –

6
  • «Что оправдывает стандарт C++, запрещающий неконстантные ссылки таким образом?»

Практический опыт использования противоположного соглашения, в котором все было изначально. C++ - это в значительной степени развитый язык, а не разработанный. Во многом все еще существуют правила, которые, как оказалось, работают (хотя некоторые исключения BIG произошли со стандартизацией 1998 года, например, позорный export, где комитет изобрел, а не стандартизовал существующую практику).

Для правила привязки имелось не только опыт работы на C++, но и аналогичный опыт с другими языками, такими как Fortran.

Как замечает @j_random_hacker в своем ответе (который, как я писал, был забит 0, показывая, что подсчет очков в SO действительно не работает как показатель качества), самые серьезные проблемы связаны с неявными преобразованиями и разрешение перегрузки.

  • «Как я могу заставить GCC разрешить этот код?»

Вы не можете.

Вместо того, чтобы ...

bootrec_reset(File(path, size, off), blksize); 

... написать ...

File f(path, size, off); 
bootrec_reset(f, blksize); 

Или определить соответствующую перегрузку bootrec_reset. Или, если «умные» призывы кода, вы можете в принципе написать bootrec_reset(tempref(File(path, size, off)), blksize);, где вы просто определяете tempref, чтобы вернуть ссылку на аргумент соответствующим образом const-casted. Но даже если это техническое решение, не надо.

  • «Есть ли предстоящее стандартное изменение C++ 0x это в любом случае, или есть что-то новый стандарт дает мне, что более уместно, например, все, что jibberish о ссылках RValue?»

Нет, ничего не меняет вещи для данного кода.

Если вы хотите переписать, то вы можете использовать, например. C++ 0x rvalue, или обходные решения C++ 98, показанные выше.

Приветствия & НТН.,

6

Does the upcoming C++0x standard change this in anyway, or is there something the new standard gives me that is more appropriate here, for example all that jibberish about rvalue references?

Да. Так как каждое имя именующее, почти тривиальный рассматривать любое выражение, как будто это было именующим:

template <typename T> 
T& as_lvalue(T&& x) 
{ 
    return x; 
} 

// ... 

bootrec_reset(as_lvalue(File(path, size, off)), blksize); 
1

В качестве альтернативы, просто перегрузка.

static void bootrec_reset(File &&file, ssize_t blksize) { 
    return bootrec_reset(file, blksize); 
} 

Это самое простое решение.

0

How can I force GCC to permit this code?

Если у вас есть определение файла, то вы можете попробовать играть трюки, такие как этот:

class File /* ... */ { 
public: 
    File* operator&() { return this; } 
/* ... */ 
}; 
/* ... */ 
bootrec_reset(*&File(path, size, off), blksize); 

Это компилирует для меня в режиме 98 C++.

Does the upcoming C++0x standard change this in anyway, or is there something the new standard gives me that is more appropriate here, for example all that jibberish about rvalue references?

Очевидно, что это путь, если это вообще возможно.

Смежные вопросы