Tenho uma classe modelo que contém uma tupla.
template <class... SomeTypes>
class Foo
{ ... };
Como posso aplicar uma restrição aos tipos contidos no pacote de parâmetros SomeTypes? Por exemplo, se eu quisesse garantir que todos os tipos se encaixassem no std::movable
conceito?
E se SomeTypes
também houvesse modelos?
template < template<typename> class... SomeTypes>
class Bar
{ ... };
Aqui está um caso mais específico do que estou procurando. O código não deveria (e não compila) porque FooB
não é copiável.
Infelizmente, o compilador não exibe as melhores mensagens de erro quando isso acontece (no código com o qual estou lidando, os tipos são mais complexos e isso resulta em listas de erros gigantescas). Espero poder adicionar algum tipo de requires
cláusula FooContainer
que gere um erro muito mais claro.
#include <tuple>
#include <concepts>
template <typename ContainerType>
class Foo
{
};
template <typename ContainerType>
class FooA : public Foo<ContainerType>
{
public:
int some_data {0};
void DoSomething()
{
some_data++;
}
};
template <typename ContainerType>
class FooB : public Foo<ContainerType>
{
public:
// non-copyable object
FooB() = default;
FooB( const FooB& ) = delete;
FooB& operator=( const FooB& ) = delete;
~FooB() = default;
int some_data {0};
void DoSomething()
{
some_data++;
}
};
template < template<typename> class... SomeTypes>
// requires (std::copyable<SomeTypes...>) ???
class FooContainer
{
public:
std::tuple<SomeTypes<FooContainer>...> my_tuple;
template <std::size_t I = 0, std::enable_if_t<I == sizeof...(SomeTypes)>* = nullptr>
void DoAllThings()
{
}
template <std::size_t I = 0, std::enable_if_t<I < sizeof...(SomeTypes)>* = nullptr>
void DoAllThings()
{
std::get<I>(my_tuple).DoSomething();
DoAllThings<I + 1>();
}
};
using MyContainer = FooContainer<FooA, FooB>;
int main()
{
MyContainer my_bar;
my_bar.DoAllThings();
MyContainer my_bar2 = my_bar;
}
--Edição 2--
Então, depois de brincar com algumas das sugestões aqui, o mais próximo que cheguei do que eu queria foi usar uma função estática externa para fazer a verificação.
template <typename FooType>
requires (std::copyable<FooType>)
constexpr bool FooTypeCheck()
{
return true;
}
static_assert(FooTypeCheck<FooA<int>>());
Isso me dá a verificação que eu quero (se a classe Foo derivada é copiável) e, portanto, a saída do compilador mais fácil de ler se a verificação falhar. Para fins de verificação de tipo, o ContainerType não importa, então eu apenas passo um int. Infelizmente, isso também significa que preciso adicionar o static_assert para cada classe Foo derivada, e o int que eu passo para ContainerType é um pouco estranho.
--Edição Final (Resposta)-- Dei os créditos a @Ted Lyngmo pela resposta, pois estava próxima do que eu precisava. Como o parâmetro ContainerType é apenas parte do CRTP, ele realmente não importa para os propósitos da restrição, e posso usar apenas int, e a solução é um pouco mais simples.
template <template <typename> class FooType>
concept FooTypeCheck = std::copyable<FooType<int>>;
template < template<typename> class... SomeTypes>
requires (... && FooTypeCheck<SomeTypes>)
class FooContainer
{ ... };