Expected<T>
é implementado em llvm / Support / Error.h. É uma união com tags que contém a T
ou an Error
.
Expected<T>
é uma classe de modelo com o tipo T
:
template <class T> class LLVM_NODISCARD Expected
Mas esses dois construtores realmente me confundem:
/// Move construct an Expected<T> value from an Expected<OtherT>, where OtherT
/// must be convertible to T.
template <class OtherT>
Expected(Expected<OtherT> &&Other,
typename std::enable_if<std::is_convertible<OtherT, T>::value>::type
* = nullptr) {
moveConstruct(std::move(Other));
}
/// Move construct an Expected<T> value from an Expected<OtherT>, where OtherT
/// isn't convertible to T.
template <class OtherT>
explicit Expected(
Expected<OtherT> &&Other,
typename std::enable_if<!std::is_convertible<OtherT, T>::value>::type * =
nullptr) {
moveConstruct(std::move(Other));
}
Por que Expected<T>
repetir duas construções para a mesma implementação? Por que não faz assim ?:
template <class OtherT>
Expected(Expected<OtherT>&& Other) { moveConstruct(std::move(Other));}
explicit
palavraexplicit
palavras-chave são importantes aqui? Alguém poderia dar um exemplo?Respostas:
Porque esse construtor é condicionalmente explícito de acordo com a proposta. Isso significa que o construtor é explícito apenas se alguma condição for atendida (aqui, conversibilidade de
T
eOtherT
).O C ++ não possui um mecanismo para essa funcionalidade (algo como
explicit(condition)
) antes do C ++ 20. Portanto, as implementações precisam usar algum outro mecanismo, como uma definição de dois construtores diferentes - um explícito e outro a conversão - e garantir a seleção do construtor apropriado de acordo com a condição. Isso geralmente é feito via SFINAE com a ajuda destd::enable_if
, onde a condição é resolvida.Desde C ++ 20, deve haver uma versão condicional do
explicit
especificador. A implementação seria muito mais fácil com uma única definição:fonte
Para entender isso, devemos começar
std::is_convertible
. De acordo com a cppreference :A parte importante aqui é que ele verifica apenas conversões implícitas. Portanto, o que as duas implementações no seu OP significam é que, se
OtherT
é implicitamente convertível emT
, entãoexpected<OtherT>
é implicitamente convertível emexpected<T>
. SeOtherT
requer uma conversão explícita paraT
,Expected<OtherT>
exige uma conversão explícita paraExpected<T>
.Aqui estão exemplos de elencos implícitos e explícitos e suas
Expected
contrapartesfonte
Expected<OtherT>
requer um elenco explícito paraExpected<T>
significar'. O que significa o 'elenco explícito' aqui? Não consigo imaginar um exemplo disso.