2016-11-02 7 views
3

Обычно, когда функция возвращает boost::optional Я видел много людей, возвращающих пустую фигуру {}, чтобы обозначить пустое значение, которое отлично работает и короче, чем возвращение boost::none.Разница между boost и std :: экспериментальное необязательное назначение

Я попытался сделать что-то похожее на пустой boost::optional<int>, но при вызове оператора присваивания копии (или, скорее всего, операции присваивания операции op) с пустой скобкой в ​​правой части пустая скобка преобразуется в int, а затем это значение присваивается необязательному, поэтому я заканчиваю тем, что переменная установлена ​​в 0, а не пустая величина, как я ожидал. Вот пример https://godbolt.org/g/HiF92v, если я попробую то же самое с std::experimental::optional Я получаю результат, который я ожидаю (просто замените на std :: experimental :: optional в примере, и вы увидите, что инструкция становится mov eax, eax).

Также, если я попытаюсь использовать другой аргумент шаблона для форсирования необязательный (не целочисленный тип), некоторые компиляторы компилируют (с поведением, которое я ожидаю, пример здесь http://cpp.sh/5j7n), а другие нет. Таким образом, даже для одной и той же библиотеки поведение отличается от шаблона arg.

Я хотел бы понять, что здесь происходит, я знаю, что это связано с тем, что я использую функцию C++ 14 для библиотеки, которая не учитывает это в дизайне. Я прочитал заголовок boost/optional, но я потерялся в деталях, я также попытался изучить скомпилированный код без встраивания с аналогичным результатом.

Я использую gcc 4.9.2 с -std = C++ 14 и повышаю 1.57.

btw: Я знал, что должен был использовать boost::optional::reset или boost::none, но я старался соответствовать семантике в остальной части базы кода.

+0

Существует разница между назначением и инициализацией ... – aschepler

+0

да, но вопрос в том, почему назначение отличается между boost :: optional и std :: experimental :: optional и почему эта работа для boost :: optional с не целочисленными типами – dlavila

+1

В принципе, 'std :: experimental :: optional' выполняет некоторые специальные трюки, чтобы гарантировать, что' o = {} 'всегда сбрасывает его. 'boost :: optional' не имеет этих трюков. –

ответ

5

Чтобы понять, что происходит, рассмотрим следующий пример первый:

void fun(int) { puts("f int"); } 
void fun(double) { puts("f double"); } 

int main() { 
    fun({}); // error 
} 

Это приводит к ошибке компиляции, потому что разрешение перегрузки неубедительны: double и int подходят одинаково хорошо. Но, если нескалярный тип входит в игру, ситуация иная:

struct Wrap{}; 
void fun(int) { puts("f(int)"); } 
void fun(Wrap) { puts("f(Wrap)"); } 

int main() { 
    fun({}); // ok: f(int) selected 
} 

Это потому, что скалярная лучше подходит. Если по какой-то причине, я хочу те же две перегрузки, но в то же время я хотел бы fun({}) выбрать перегрузку fun(Wrap), я могу настроить Определения бит:

template <typename T> 
std::enable_if_t<std::is_same<int, std::decay_t<T>>::value> 
fun(T) { puts("f int"); } 

void fun(Wrap) { puts("f(Wrap)"); } 

То есть, fun(Wrap) остается неизменным, но первая перегрузка теперь является шаблоном, который принимает любые T. Но с enable_if мы ограничиваем его, так что он работает только с типом int. Итак, это довольно «искусственный» шаблон, но он выполняет эту работу. Если я позвоню:

fun(0); // picks fun(T) 

Искусственный шаблон выбирается. Но если я типа:

fun({}); // picks fun(Wrap) 

Искусственный шаблон еще шаблон, поэтому он никогда не рассматривается как в типе вычета в этом случае, и единственной видимой перегрузки fun(Wrap), поэтому он получает выбран.

Этот же трюк используется в std::optional<T>: у него нет задания от T. Вместо этого он имеет аналогичный искусственный шаблон назначения, который принимает любой U, но позже ограничен, так что T == U. Вы можете увидеть это в справочной реализации here.

boost::optional<T> был реализован до C++ 11, не подозревая об этом «идиоме сброса». Поэтому он имеет нормальное назначение от T, а в случаях, когда T оказывается скаляром, это назначение от T является предпочтительным. Отсюда и различие.

Учитывая все это, я думаю, что Boost.Optional имеет ошибку, что она делает что-то противоположное std::optional. Даже если он не реализуется в Boost.Optional, он должен, по крайней мере, не скомпилировать, чтобы избежать непредвиденных неожиданностей.