AskOverflow.Dev

AskOverflow.Dev Logo AskOverflow.Dev Logo

AskOverflow.Dev Navigation

  • Início
  • system&network
  • Ubuntu
  • Unix
  • DBA
  • Computer
  • Coding
  • LangChain

Mobile menu

Close
  • Início
  • system&network
    • Recentes
    • Highest score
    • tags
  • Ubuntu
    • Recentes
    • Highest score
    • tags
  • Unix
    • Recentes
    • tags
  • DBA
    • Recentes
    • tags
  • Computer
    • Recentes
    • tags
  • Coding
    • Recentes
    • tags
Início / coding / Perguntas / 79565037
Accepted
Rob W.
Rob W.
Asked: 2025-04-10 02:03:30 +0800 CST2025-04-10 02:03:30 +0800 CST 2025-04-10 02:03:30 +0800 CST

Fornecendo restrições para um pacote de parâmetros

  • 772

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::movableconceito?

E se SomeTypestambé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 FooBnã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 requirescláusula FooContainerque 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
{ ... };
c++
  • 3 3 respostas
  • 172 Views

3 respostas

  • Voted
  1. Best Answer
    Ted Lyngmo
    2025-04-10T02:54:48+08:002025-04-10T02:54:48+08:00

    A maneira mais direta seria usar o std::movableconceito:

    #include <concepts>
    
    template <std::movable... SomeTypes>
    class Foo {
        // ...
    };
    

    E se SomeTypes também fosse modelos?

    Os modelos de classe podem ser especializados e não é possível verificar se um modelo de classe é móvel para todos os tipos. Você precisa de instâncias reais dos modelos de classe para poder verificar a mobilidade.

    Por exemplo, se você quiser verificar se os modelos de classe fornecidos se tornam móveis quando instanciados com inte std::string, você pode verificar isso assim:

    template<template <class> class T, class... Ts>
    concept MovableWith = (... && std::movable<T<Ts>>);
    
    template <template <class> class... SomeTypes>
        requires (... && MovableWith<SomeTypes, int, std::string>)
    class Bar {
        // ...
    };
    
    • 6
  2. Alon Alush
    2025-04-10T02:05:27+08:002025-04-10T02:05:27+08:00

    Você pode colocar uma cláusula requires no modelo:

    #include <concepts> // For std::movable
    
    template <class... SomeTypes>
    requires (std::movable<SomeTypes> && ...)
    class Foo
    {
        // ...
    };
    

    Se preferir manter os requisitos fora da definição da classe, você pode fazer algo como:

    #include <concepts>
    
    template <class... Types>
    concept AllMovable = (std::movable<Types> && ...);
    
    template <AllMovable... SomeTypes>
    class Foo
    {
        // ...
    };
    
    • 5
  3. edrezen
    2025-04-10T15:56:42+08:002025-04-10T15:56:42+08:00

    A seguir, respondemos ao caso mais estranho quando SomeTypesse trata de uma classe de modelo.

    Você pode verificar alguma propriedade em uma classe, mas isso não é possível em uma classe de modelo, FooApois ela não é um tipo (representa uma família de tipos). Ela se torna um tipo real quando você a instancia, fornecendo argumentos de tipo aos seus parâmetros de modelo. Por exemplo, você pode verificar alguma propriedade de tipo para FooA<int>, mas não para FooAem geral.

    Então você pode fornecer DoAllThingsesse argumento de modelo e então fazer algumas verificações de conceito (por exemplo, se os tipos são construtíveis por padrão):

    template < template<typename> class... SomeTypes>
    struct FooContainer
    {
        template <typename ContainerType>
        requires (... && std::is_default_constructible_v<SomeTypes<ContainerType>>)
        void DoAllThings()
        {
            [&] <std::size_t...Is> (std::index_sequence<Is...>)  {
                (SomeTypes<ContainerType>{}.DoSomething(), ...);
            } (std::make_index_sequence<sizeof...(SomeTypes)>());
        }
    };
    

    e usá-lo como

    template <typename ContainerType>
    struct FooA
    {
        void DoSomething()  {}
    };
    
    template <typename ContainerType>
    struct FooB
    {
        FooB (int d) : some_data(d) {}  // => no more default constructible
        int some_data {0};
        void DoSomething()  {}
    };
    
    
    int main()
    {
        FooContainer<FooA,FooB> my_bar;
        my_bar.DoAllThings<int>();
        my_bar.DoAllThings<double>();
    }
    

    Como esperado, isso produziria um erro referente à característica de tipo std::is_default_constructibleem nosso exemplo

    <source>:37:12: error: no matching member function for call to 'DoAllThings'
       37 |     my_bar.DoAllThings<int>();
          |     ~~~~~~~^~~~~~~~~~~~~~~~
    <source>:11:10: note: candidate template ignored: constraints not satisfied [with ContainerType = int]
       11 |     void DoAllThings()
          |          ^
    <source>:10:22: note: because 'std::is_default_constructible_v<FooB<int> >' evaluated to false
       10 |     requires (... && std::is_default_constructible_v<SomeTypes<ContainerType>>)
    

    Demonstração

    • 1

relate perguntas

  • Por que os compiladores perdem a vetorização aqui?

  • Erro de compilação usando CMake com biblioteca [fechada]

  • Erro lançado toda vez que tento executar o premake

  • Como criar um tipo de octeto semelhante a std::byte em C++?

  • Somente operações bit a bit para std::byte em C++ 17?

Sidebar

Stats

  • Perguntas 205573
  • respostas 270741
  • best respostas 135370
  • utilizador 68524
  • Highest score
  • respostas
  • Marko Smith

    Reformatar números, inserindo separadores em posições fixas

    • 6 respostas
  • Marko Smith

    Por que os conceitos do C++20 causam erros de restrição cíclica, enquanto o SFINAE antigo não?

    • 2 respostas
  • Marko Smith

    Problema com extensão desinstalada automaticamente do VScode (tema Material)

    • 2 respostas
  • Marko Smith

    Vue 3: Erro na criação "Identificador esperado, mas encontrado 'import'" [duplicado]

    • 1 respostas
  • Marko Smith

    Qual é o propósito de `enum class` com um tipo subjacente especificado, mas sem enumeradores?

    • 1 respostas
  • Marko Smith

    Como faço para corrigir um erro MODULE_NOT_FOUND para um módulo que não importei manualmente?

    • 6 respostas
  • Marko Smith

    `(expression, lvalue) = rvalue` é uma atribuição válida em C ou C++? Por que alguns compiladores aceitam/rejeitam isso?

    • 3 respostas
  • Marko Smith

    Um programa vazio que não faz nada em C++ precisa de um heap de 204 KB, mas não em C

    • 1 respostas
  • Marko Smith

    PowerBI atualmente quebrado com BigQuery: problema de driver Simba com atualização do Windows

    • 2 respostas
  • Marko Smith

    AdMob: MobileAds.initialize() - "java.lang.Integer não pode ser convertido em java.lang.String" para alguns dispositivos

    • 1 respostas
  • Martin Hope
    Fantastic Mr Fox Somente o tipo copiável não é aceito na implementação std::vector do MSVC 2025-04-23 06:40:49 +0800 CST
  • Martin Hope
    Howard Hinnant Encontre o próximo dia da semana usando o cronógrafo 2025-04-21 08:30:25 +0800 CST
  • Martin Hope
    Fedor O inicializador de membro do construtor pode incluir a inicialização de outro membro? 2025-04-15 01:01:44 +0800 CST
  • Martin Hope
    Petr Filipský Por que os conceitos do C++20 causam erros de restrição cíclica, enquanto o SFINAE antigo não? 2025-03-23 21:39:40 +0800 CST
  • Martin Hope
    Catskul O C++20 mudou para permitir a conversão de `type(&)[N]` de matriz de limites conhecidos para `type(&)[]` de matriz de limites desconhecidos? 2025-03-04 06:57:53 +0800 CST
  • Martin Hope
    Stefan Pochmann Como/por que {2,3,10} e {x,3,10} com x=2 são ordenados de forma diferente? 2025-01-13 23:24:07 +0800 CST
  • Martin Hope
    Chad Feller O ponto e vírgula agora é opcional em condicionais bash com [[ .. ]] na versão 5.2? 2024-10-21 05:50:33 +0800 CST
  • Martin Hope
    Wrench Por que um traço duplo (--) faz com que esta cláusula MariaDB seja avaliada como verdadeira? 2024-05-05 13:37:20 +0800 CST
  • Martin Hope
    Waket Zheng Por que `dict(id=1, **{'id': 2})` às vezes gera `KeyError: 'id'` em vez de um TypeError? 2024-05-04 14:19:19 +0800 CST
  • Martin Hope
    user924 AdMob: MobileAds.initialize() - "java.lang.Integer não pode ser convertido em java.lang.String" para alguns dispositivos 2024-03-20 03:12:31 +0800 CST

Hot tag

python javascript c++ c# java typescript sql reactjs html

Explore

  • Início
  • Perguntas
    • Recentes
    • Highest score
  • tag
  • help

Footer

AskOverflow.Dev

About Us

  • About Us
  • Contact Us

Legal Stuff

  • Privacy Policy

Language

  • Pt
  • Server
  • Unix

© 2023 AskOverflow.DEV All Rights Reserve