Estou explorando o recurso "deduzindo isso" e estou com dificuldades para entender como usá-lo corretamente com conceitos. Aqui está o que eu tenho:
#include <concepts>
struct X {
X() = default;
X& operator+=(int x) { value += x; return *this; }
int value = 0;
};
struct Y {
Y() = default;
template<typename Self> Self& operator+=(this Self& self, int x) {
self.value += x; return self;
}
int value = 0;
};
template<typename T, typename U>
concept has_addition_assignment =
std::is_lvalue_reference_v<T>
&& requires(T a, U b) { { a += b } -> std::same_as<T>; };
static_assert(has_addition_assignment<X&,int>); // OK
static_assert(!has_addition_assignment<const X&,int>); // OK
static_assert(has_addition_assignment<Y&,int>); // OK
// static_assert(!has_addition_assignment<const Y&,int>); // Fails
int
main()
{
// Not allowed.
// const Y y;
// y += 1;
}
O conceito é tentar garantir que X x; x += 1;
funcione, mas const X x; x += 1
não funciona. Isso funciona para, X
mas não funciona Y
, apesar de const Y y; y += 1;
falhar.
Acredito que meu conceito está escrito incorretamente ou talvez +=
não Y
expresse minha intenção corretamente. Qual é a melhor maneira de verificar se não é aceitável adicionar algo a um objeto constante ao usar deducing this?
Os conceitos só podem detectar se uma declaração representa um código válido, não se a definição por trás dessa declaração funciona.
X::operator +=
possui uma declaração que aceitathis
como não-const
. Não há nenhuma outra sobrecarga da função. Portanto, no nível da declaração, qualquer tentativa de chamar esta função com umconst
valor falhará devido à falta de uma sobrecarga apropriada que possa aceitar umconst
this
.Como você declarou
Y::operator +=
como um modelo irrestrito,Self
poderia ser qualquer coisa . Poderia ser umconst
tipo, e o restante da declaração não saberia nem se importaria. Tentar chamar a função em umconst
objeto ainda falhará, mas só falhará devido à falha na definição (tentativa de modificar umconst
objeto). No que diz respeito à declaração, ela é legítima.E os conceitos só se importam com a declaração.
Portanto, se você pretende que sua
Self
função não seja apropriada paraconst
parâmetros, precisa codificá-los explicitamente na declaração. Você poderia usar um conceito que falha se o tipo for umconst
tipo, mas uma maneira mais simples é explicitamente especificar= delete
aconst
versão:Como essa é uma correspondência melhor, ela será usada se um
const
tipo estiver sendo empregado. E isso causará uma falha na resolução de sobrecarga (um teste baseado em declaração).