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 / 79422181
Accepted
Fedor
Fedor
Asked: 2025-02-08 05:17:43 +0800 CST2025-02-08 05:17:43 +0800 CST 2025-02-08 05:17:43 +0800 CST

Contando o número de valores presentes em uma matriz de opcionais com std::ranges

  • 772

Meu colega porta um programa C++ com intervalos no macOS e observa um erro de compilação inesperado.

Após a simplificação máxima, o programa de exemplo fica assim:

#include <optional>
#include <algorithm>

int main() {
    std::optional<int> ops[4];
    //...
    return (int)std::ranges::count_if( ops, &std::optional<int>::has_value );
};

O GCC e o MSVC funcionam bem com o programa, mas o Clang exibe um longo erro:

 error: no matching function for call to object of type 'const __count_if::__fn'
    7 |     return (int)std::ranges::count_if( ops, &std::optional<int>::has_value );
      |                 ^~~~~~~~~~~~~~~~~~~~~
/opt/compiler-explorer/clang-19.1.0/bin/../include/c++/v1/__algorithm/ranges_count_if.h:62:3: note: candidate template ignored: constraints not satisfied [with _Range = std::optional<int> (&)[4], _Proj = identity, _Predicate = bool (std::__optional_storage_base<int>::*)() const noexcept]
   62 |   operator()(_Range&& __r, _Predicate __pred, _Proj __proj = {}) const {
      |   ^
/opt/compiler-explorer/clang-19.1.0/bin/../include/c++/v1/__algorithm/ranges_count_if.h:60:13: note: because 'indirect_unary_predicate<_Bool (std::__optional_storage_base<int>::*)() const noexcept, projected<iterator_t<optional<int> (&)[4]>, identity> >' evaluated to false
   60 |             indirect_unary_predicate<projected<iterator_t<_Range>, _Proj>> _Predicate>
      |             ^
/opt/compiler-explorer/clang-19.1.0/bin/../include/c++/v1/__iterator/concepts.h:191:60: note: because 'predicate<_Bool (std::__optional_storage_base<int>::*&)() const noexcept, iter_value_t<__type> &>' evaluated to false
  191 |     indirectly_readable<_It> && copy_constructible<_Fp> && predicate<_Fp&, iter_value_t<_It>&> &&
      |                                                            ^
/opt/compiler-explorer/clang-19.1.0/bin/../include/c++/v1/__concepts/predicate.h:28:21: note: because 'regular_invocable<_Bool (std::__optional_storage_base<int>::*&)() const noexcept, std::optional<int> &>' evaluated to false
   28 | concept predicate = regular_invocable<_Fn, _Args...> && __boolean_testable<invoke_result_t<_Fn, _Args...>>;
      |                     ^
/opt/compiler-explorer/clang-19.1.0/bin/../include/c++/v1/__concepts/invocable.h:34:29: note: because 'invocable<_Bool (std::__optional_storage_base<int>::*&)() const noexcept, std::optional<int> &>' evaluated to false
   34 | concept regular_invocable = invocable<_Fn, _Args...>;
      |                             ^
/opt/compiler-explorer/clang-19.1.0/bin/../include/c++/v1/__concepts/invocable.h:28:3: note: because 'std::invoke(std::forward<_Fn>(__fn), std::forward<_Args>(__args)...)' would be invalid: no matching function for call to 'invoke'
   28 |   std::invoke(std::forward<_Fn>(__fn), std::forward<_Args>(__args)...); // not required to be equality preserving
      |   ^
/opt/compiler-explorer/clang-19.1.0/bin/../include/c++/v1/__algorithm/ranges_count_if.h:54:3: note: candidate function template not viable: requires at least 3 arguments, but 2 were provided
   54 |   operator()(_Iter __first, _Sent __last, _Predicate __pred, _Proj __proj = {}) const {
      |   ^          ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~

Demonstração online: https://gcc.godbolt.org/z/no55zPzGz

Não entendi o que há de errado com o programa?

c++
  • 1 1 respostas
  • 233 Views

1 respostas

  • Voted
  1. Best Answer
    Barry
    2025-02-08T05:32:27+08:002025-02-08T05:32:27+08:00

    O que você está fazendo é tecnicamente UB e pode falhar — não há garantia de que usar ponteiros para funções de membro de classes de biblioteca padrão como essa realmente funcione. Para libc++, não funciona.

    Concretamente, a questão recebe uma hierarquia como:

    struct B { bool has_value() const; }
    struct D : B { };
    

    O tipo de &B::has_valueé obviamente um bool (B::*)() const. Mas o tipo de &D::has_value... também é a mesma coisa. Pessoalmente, acho que é um defeito de linguagem e deveria lhe dar um bool (D::*)() constjá que foi isso que você pediu — mas isso provavelmente não pode ser mudado agora, e essas são as regras.

    Agora, para libstdc++ e MSVCSTL, &std::optional<int>::has_valuedá a você um bool (std::optional<int>::*)() constporque eles aparentemente implementam essa função membro diretamente. Mas para libc++, eles aparentemente implementam seu optionalum pouco, mas diferentemente... então o ponto membro que você obtém de volta é, na verdade, um bool (std::__optional_storage_base<int>::*)() const. Bem, também noexcept, mas isso não importa.

    Agora você pode pensar que isso não importa — afinal, você pode invocar funções de membro de classe base sem problemas, certo? Você pode. A menos que seja uma base privada. O que, neste caso, é.

    Em forma reduzida, libstdc++ e MSVCSTL se parecem com isto:

    template <class T> struct optional { bool has_value() const; };
    

    Enquanto libc++ se parece com isto:

    template <class T> struct optional_base { bool has_value() const; };
    template <class T> struct optional : private optional_base<T> {
      using optional_base<T>::has_value;
    };
    

    O resultado é que, embora o.has_value()funcione para todas as implementações, tentar usar &optional<int>::has_valuefor libc++ não é invocável porque você obtém um ponteiro para uma função de classe base privada.


    Aliás, uma das razões pelas quais seria ótimo se ponteiros para membros fossem invocáveis ​​é a qualidade das mensagens de erro.

    Considerar:

    template <class F, class T>
    concept invocable = requires (F f, T t) {
        #if DIRECT
        (t.*f)();
        #else
        std::invoke(f, t);
        #endif
    };
    
    static_assert(invocable<decltype(&std::optional<int>::has_value), std::optional<int>>);
    

    No clang, esse conceito falha de qualquer forma, já que está verificando a mesma coisa de qualquer forma. Mas a qualidade do erro é bem diferente. Com std::invoke:

    <source>:7:5: note: because 'std::invoke(f, t)' would be invalid: no matching function for call to 'invoke'
        7 |     std::invoke(f, t);
          |     ^
    

    Com (t.*f)():

    <source>:7:7: note: because '(t .* f)()' would be invalid: cannot cast 'std::optional<int>' to its private base class 'std::__optional_storage_base<int>'
        7 |     (t.*f)();
          |       ^
    

    A disparidade do gcc é similar (embora você tenha que fazer fconcepts-diagnostics-depth=2). Com a invocação direta, você obtém um diagnóstico sobre a classe base estar inacessível. Com invokevocê obtém... nada.

    O que você prefere ver?

    • 13

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