Este código compila:
#include <iostream>
class MyClass;
template<typename T>
concept HasFun = requires(T t)
{
{ t.fun() };
};
int main()
{
std::cout << HasFun<MyClass> << std::endl;
return 0;
}
sem erro e imprime 0
.
Quando
class MyClass;
é substituído por
class MyClass
{
public:
void fun();
};
ele imprime 1
;
Minha expectativa é que a compilação falhe (no primeiro caso) com um erro dizendo algo como "agregado 'MyClass' tem tipo incompleto".
Qual é o caso de uso em permitir que conceitos avaliem declarações de classe?
Não, isso não aconteceria. Uma require-expression verifica se
t.fun()
é uma expressão válida, e não é válida porquet
tem um tipo incompleto. No entanto, isso significa simplesmente que a expressão éfalse
, não que o programa é malformado.Embora "funcione", usar conceitos com tipos incompletos é bem perigoso. Veja Can a concept be checked against incomplete type . Para resumir os problemas:
requires
em um conceito) não produzem o mesmo resultado em todos os lugares , o programa está malformado e não é necessário diagnóstico ( [temp.constr.atomic] ).std::is_trivially_copyable
levam a UB quando usadas com um tipo incompleto ( [meta.unary.prop] ).No seu caso, o programa seria malformado, sem necessidade de diagnóstico, se você primeiro verificasse
HasFun<MyClass>
com incompleteMyClass
e verificasse novamente depois com completeMyClass
. Na prática, os compiladores podem armazenar em cache o resultado deHasFun<MyClass>
para que eles realizem apenas uma verificação, e você obtenha resultados falsos se esse resultado armazenado em cache estiver incorreto.Este é certamente um grande argumento, mas alguns conceitos podem ser usados com tipos incompletos sem problemas:
É perfeitamente razoável ter contêineres de tipos incompletos. Por exemplo,
std::vector<T>
não requer um completeT
(desde C++11), mas operações comopush_back
requerem um tipo completo.Para responder algumas das minhas perguntas que ficaram sem resposta:
não, declarar classes adiante não é o problema - imagino que em alguns cenários poderia ser (se conceitos forem usados em expressões que operam em tipos e não em instâncias reais de objetos), mas esse não é o caso na minha base de código (eu uso conceitos para restringir argumentos de função para que as definições de classe tenham garantia de estar disponíveis)
o que procurar é herança (incluindo CRTP) - a discussão na questão relacionada que Jan vinculou aborda diretamente o problema que tenho; é óbvio para mim agora, eu só queria que o compilador me desse um aviso, pois ainda não consigo imaginar um cenário em que alguém iria querer fazer isso intencionalmente