Niejawna konwersja niedozwolona po powrocie


21
#include <optional>

bool f() {
  std::optional<int> opt;
  return opt;
}

Nie kompiluje: 'return': cannot convert from 'std::optional<int>' to 'bool'

Odniesienie do konsultacji Chciałbym znaleźć wyjaśnienie, ale przeczytałem je tak, jak powinno być dobrze.

Konwersje niejawne są wykonywane za każdym razem, gdy wyrażenie pewnego typu T1 jest używane w kontekście, który nie akceptuje tego typu, ale akceptuje niektóre inne typy T2; w szczególności:

  • gdy wyrażenie jest używane jako argument podczas wywoływania funkcji zadeklarowanej za pomocą T2 jako parametru;
  • gdy wyrażenie jest używane jako operand z operatorem, który oczekuje T2;
  • podczas inicjowania nowego obiektu typu T2, w tym instrukcji return w funkcji zwracającej T2;
  • gdy wyrażenie jest używane w instrukcji switch (T2 jest typem integralnym);
  • gdy wyrażenie jest używane w instrukcji if lub w pętli (T2 to bool).

7
Niejawne konwersje są wykonywane” , ale operator bool()o std::optionalto explicit.
Jarod42

Odpowiedzi:


22

std::optionalnie ma możliwości niejawnej konwersji na bool. (Zezwalanie na niejawne konwersje booljest ogólnie uważane za zły pomysł, ponieważ booljest to typ integralny, więc coś w rodzaju int i = optskompilowałoby się i zrobiłoby całkowicie niewłaściwą rzecz).

std::optional nie mają „kontekstowe” do konwersji Bool, definicja, która wygląda podobnie do operatora Obsada: explicit operator bool(). Nie można tego użyć do niejawnych konwersji; ma zastosowanie tylko w niektórych szczególnych sytuacjach, w których oczekiwany „kontekst” jest wartością logiczną, jak warunek instrukcji if.

To czego chcesz opt.has_value().


4

Z dokumentów C ++ :

Gdy obiekt typu opcjonalny <T> jest kontekstowo konwertowany na bool, konwersja zwraca true, jeśli obiekt zawiera wartość, i false, jeśli nie zawiera wartości.

Przeczytaj o konwersjach kontekstowych tutaj :

W następujących kontekstach oczekiwany jest typ bool i niejawna konwersja jest wykonywana, jeśli deklaracja bool t (e); jest dobrze sformułowany (to znaczy jawna funkcja konwersji, taka jak jawna T :: operator bool () const; jest brana pod uwagę). Mówi się, że takie wyrażenie e jest kontekstowo konwertowane na bool.

  • kontrolujące wyrażenie if, while, for;
  • operandy wbudowanych operatorów logicznych !, && i ||;
  • pierwszy operand operatora warunkowego?:;
  • predykat w deklaracji static_assert;
  • wyrażenie w specyfikatorze noexcept;
  • wyrażenie w wyraźnym specyfikatorze;

Możesz wykonać następujący hack:

bool f() {
    std::optional<int> opt;
    return opt || false;
}

ponieważ konwersja kontekstowa ma miejsce w przypadku wbudowanych operatorów logicznych, ale konwersja kontekstowa nie zawiera returninstrukcji i std::optionalsama w sobie nie ma niejawnej konwersji na bool.

Dlatego najlepiej byłoby użyć std::optional<T>::has_value:

bool f() {
    std::optional<int> opt;
    return opt.has_value();
}

co return {opt}? lubreturn bool{opt};
darune

3
@darune return {opt};nie będzie działać, ale return static_cast<bool>(opt);czy return bool{opt};będzie działać. Sugeruje się jednak użycie has_valuefunkcji członka, ponieważ naprawdę pokazuje wyraźną intencję tego, co chcesz zrobić
NutCracker

Lub słynny return !!pot;hack ( has_valuelepszy)
LF


1

Tak naprawdę nie chodzi o niejawną konwersję, chodzi o rodzaj inicjalizacji.

To, co opcjonalne, ma jawną funkcję konwersji, tj

explicit operator bool() const; 

Z N4849 [class.conv.fct] / p2

Funkcja konwersji może być jawna (9.2.2), w którym to przypadku jest traktowana tylko jako konwersja zdefiniowana przez użytkownika do bezpośredniej inicjalizacji.

Powyższe oznacza, że ​​w tych przypadkach będzie używana funkcja konwersji: [dcl.init] / p16

Inicjalizacja, która ma miejsce (16.1) - dla inicjalizatora, który jest nawiasową listą wyrażeń lub listą stężeń inicjowanych, (16.2) - dla nowego inicjalizatora (7.6.2.7), (16.3) - w wyrażeniu static_cast ( 7.6.1.8), (16.4) - w konwersji typu notacji funkcjonalnej (7.6.1.3) i (16.5) - w postaci stanu warunkowego nazywa się inicjowaniem bezpośrednim.

Jednak te przypadki nie będą korzystać z funkcji konwersji: [dcl.init] / p15

Inicjalizacja zachodząca w postaci inicjatora lub warunku nawiasowego lub równego (8.5), a także przekazywanie argumentów, zwracanie funkcji, zgłaszanie wyjątku (14.2), obsługa wyjątku (14.4) i inicjowanie elementu (9.4.1), nazywa się inicjowaniem kopii.

Przykład w pytaniu dotyczy przypadku inicjalizacji kopii i nie wykorzystuje opcjonalnej funkcji konwersji.

Korzystając z naszej strony potwierdzasz, że przeczytałeś(-aś) i rozumiesz nasze zasady używania plików cookie i zasady ochrony prywatności.
Licensed under cc by-sa 3.0 with attribution required.