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 / 79433032
Accepted
ChrisMM
ChrisMM
Asked: 2025-02-12 20:23:59 +0800 CST2025-02-12 20:23:59 +0800 CST 2025-02-12 20:23:59 +0800 CST

Sobrecarga ambígua somente para MSVC

  • 772

Considere o seguinte código:

#include <vector>
#include <iostream>

struct MySqlStream {
    template<class T>
    MySqlStream &operator<<( const std::vector<T> &values ) {
        std::cout << "vector";
        return *this;
    }
};

template<class T>
MySqlStream &operator<<( MySqlStream &m, const T &x ) {
    std::cout << "template";
    return m;
}

int main() {
    std::vector<double> temp;

    MySqlStream sql;

    sql << temp;
}

Tanto o g++ quanto o clang aceitam o código (e usam a vectorversão).

O MSVC, por outro lado, rejeita o código afirmando:

<source>(23): error C2593: 'operator <<' is ambiguous
<source>(6): note: could be 'MySqlStream &MySqlStream::operator <<<double>(const std::vector<double,std::allocator<double>> &)'
<source>(13): note: or       'MySqlStream &operator <<<std::vector<double,std::allocator<double>>>(MySqlStream &,const T &)'
        with
        [
            T=std::vector<double,std::allocator<double>>
        ]
<source>(23): note: while trying to match the argument list '(MySqlStream, std::vector<double,std::allocator<double>>)'

O MSVC está incorreto ao rejeitar isso? Como eu poderia contornar isso? Godbolt Link

Observação: posso usar sql.operator<<( temp );, mas isso não é muito bom quando há muitas coisas concatenadas.

c++
  • 2 2 respostas
  • 101 Views

2 respostas

  • Voted
  1. Best Answer
    aschepler
    2025-02-12T23:25:20+08:002025-02-12T23:25:20+08:00

    g++ e clang++ estão corretos, e MSVC está incorreto: o membro operator<<deve ser selecionado porque é o modelo mais especializado.

    As principais etapas na resolução de sobrecarga são:

    1. Crie um conjunto de funções candidatas. Para uma expressão de operador ( [over.match.oper] ), isso pode incluir:

      • funções membro - modelo de função "vetor" 1template<class T> MySqlStream & MySqlStream::operator<<(const std::vector<T> &);
      • funções não-membro - função "template" template 2 template<class T> MySqlStream & ::operator<<(MySqlStream &, const T &);e alguns std::operator<<modelos de função
      • candidatos integrados - representando deslocamento esquerdo inteiro
      • candidatos reescritos - nenhum neste caso
    2. Para cada função membro não estática [template] no conjunto, trate-a como se tivesse um primeiro parâmetro extra cujo tipo é uma referência ao tipo de classe ( [over.match.funcs.general]/2 ). O argumento correspondente é baseado na expressão à esquerda de .or ->, que pode envolver um this->. Neste exemplo,

      • modelo de função 1template<class T> MySqlStream & MySqlStream::operator<<(MySqlStream &, const std::vector<T> &);
    3. Para cada modelo de função no conjunto, faça a dedução do argumento do modelo para obter um tipo de função concreto ( [over.match.funcs.general]/8 , [temp.deduct] ). A dedução falha para todos os std::operator<<, então eles são descartados do conjunto. E obtemos:

      • função 1MySqlStream & operator<<(MySqlStream &, const std::vector<double> &);
      • função 2MySqlStream & operator<<(MySqlStream &, const std::vector<double> &);
    4. Verifique se cada função é "viável" ( [over.match.viable] ). Em linhas gerais, isso significa que é válido chamar a função com base em tipos de parâmetros, tipos de argumentos e restrições, mas ignorando algumas outras regras, como acesso privado ou protegido e funções excluídas. Ambas MySqlStreamas funções são viáveis. Os candidatos internos não são viáveis ​​e são descartados do conjunto. Neste ponto, apenas as duas MySqlStreamfunções permanecem.

    5. Determine a melhor função viável ( [over.match.best] ). Aqui está a peça-chave neste exemplo.

    Como os tipos de função são idênticos neste ponto, a maior parte do texto sobre Sequências de Conversão Implícitas não decide uma função melhor. A regra importante é [over.match.best.general]/(2.5) :

    ... uma função viável F 1 é definida como uma função melhor do que outra função viável F 2 se ..., e então

    • ..., ou se não isso,
    • F1e F2são especializações de modelo de função, e o modelo de função para F1é mais especializado do que o modelo de função para F2de acordo com as regras de ordenação parcial descritas em [temp.func.order] , ou se não for isso,
    • ...

    Novamente, um modelo de função membro não estático é considerado como tendo um primeiro parâmetro extra que é uma referência ao tipo de classe ( [temp.func.order]/3 ). Embora as regras exatas para isso não sejam idênticas à etapa anterior em [over.match.funcs.general]/2, neste caso, novamente obtemos o mesmo

    • modelo de função 1template<class T> MySqlStream & MySqlStream::operator<<(MySqlStream &, const std::vector<T> &);

    Nada mais em [temp.func.order] ou o relacionado [temp.deduct.partial] menciona o primeiro parâmetro especial ou o primeiro argumento para uma função membro não estática, então a única diferença importante restante são os tipos de parâmetros de função const std::vector<T> &vs. const T &. Para simplificar um pouco, vamos agora olhar para um exemplo relacionado que lida com eles:

    template<class T>
    void g(const std::vector<T> &); // g1
    
    template<class T>
    void g(const T &); // g2
    
    void test() {
        std::vector<double> temp;
        g(temp);
    }
    

    A essência de [temp.deduct.partial] é inventar argumentos de modelo tão gerais quanto possível para um modelo F1, e ver se uma chamada que corresponde exatamente à especialização da função resultante também corresponde ao outro modelo original F2. Se corresponder, F1 é "pelo menos tão especializado quanto" F2, o que significa que (quase) sempre que F1 é viável, F2 também é viável. Faça isso das duas maneiras, e se apenas uma maneira for bem-sucedida, a função que era F1 naquele teste é mais especializada.

    Então, primeiro vamos nos especializar g1: inventar um tipo T1não relacionado a nenhuma outra declaração e substituí-lo por T:

    struct T1 {};
    template void g1<T1>(const std::vector<T1> &);
    

    Em seguida, veja se g2pode ser chamado com argumentos exatamente correspondentes. Como o parâmetro aqui é uma referência lvalue, o argumento deve ser uma expressão lvalue.

    const std::vector<T1> arg1;
    g2(arg1); // arg1 as lvalue
    

    Claro, g2deduz-se que Té std::vector<T1>e é viável. g1é pelo menos tão especializado quanto g2.

    Ao contrário, inventamos um tipo struct T2 {};e o substituímos em g2:

    struct T2 {};
    template void g2<T>(const T2 &);
    

    E pode g1ser chamado com argumentos exatamente correspondentes?

    const T2 arg2;
    g1(arg2); // arg2 as lvalue
    

    Não, esse const T2argumento não é nenhuma especialização de std::vector, então a dedução do argumento de modelo para g1falha. g2não é pelo menos tão especializada quanto g1. Juntos, esses significam que g1é mais especializado que g2.

    Similarmente no exemplo original, o template de função membro "vector" 1 é o template de função mais especializado e a função mais viável, e é selecionado pela resolução de sobrecarga para a <<expressão. A resolução de sobrecarga não é ambígua como o MSVC alega.

    • 3
  2. Marek R
    2025-02-12T21:08:03+08:002025-02-12T21:08:03+08:00

    Parece que gcc e clang estão certos. Aqui está uma citação de cppreference:

    Modelo de função - cppreference.com

    • Se apenas um dos dois modelos de função sendo comparados for uma função membro, e esse modelo de função for um membro não estático de alguma classe A, um novo primeiro parâmetro é inserido em sua lista de parâmetros. Dado cv como os qualificadores cv do modelo de função e ref como o qualificador ref do modelo de função (desde C++11), o novo tipo de parâmetro é cv A& , a menos que ref seja &&, ou ref não esteja presente e o primeiro parâmetro do outro modelo tenha o tipo de referência rvalue, neste caso o tipo é cv A&& (desde C++11). Isso ajuda na ordenação de operadores, que são pesquisados ​​tanto como funções membro quanto como funções não membro:
    struct A {};
     
    template<class T>
    struct B
    {
        template<class R>
        int operator*(R&); // #1
    };
     
    template<class T, class R>
    int operator*(T&, R&); // #2
     
    int main()
    {
        A a;
        B<A> b;
        b * a; // template argument deduction for int B<A>::operator*(R&) gives R=A 
               //                             for int operator*(T&, R&), T=B<A>, R=A
     
        // For the purpose of partial ordering, the member template B<A>::operator*
        // is transformed into template<class R> int operator*(B<A>&, R&);
     
        // partial ordering between 
        //     int operator*(   T&, R&)  T=B<A>, R=A
        // and int operator*(B<A>&, R&)  R=A 
        // selects int operator*(B<A>&, A&) as more specialized
    }
    

    No seu código, o segundo argumento da função membro é const std::vector<T> &mais especializado do que const T &no caso da versão de função livre.

    Aqui estão alguns experimentos:

    • Seu código original
    • Inverter tipos de argumento de argumento (agora funções livres são preferidas) - coisa engraçada com esta versão MSVC está feliz. Então este é outro argumento para bug do MSVC.
    • 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