AskOverflow.Dev

AskOverflow.Dev Logo AskOverflow.Dev Logo

AskOverflow.Dev Navigation

  • 主页
  • 系统&网络
  • Ubuntu
  • Unix
  • DBA
  • Computer
  • Coding
  • LangChain

Mobile menu

Close
  • 主页
  • 系统&网络
    • 最新
    • 热门
    • 标签
  • Ubuntu
    • 最新
    • 热门
    • 标签
  • Unix
    • 最新
    • 标签
  • DBA
    • 最新
    • 标签
  • Computer
    • 最新
    • 标签
  • Coding
    • 最新
    • 标签
主页 / coding / 问题 / 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

仅适用于 MSVC 的模糊重载

  • 772

考虑以下代码:

#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;
}

g++ 和 clang 都接受该代码(并使用该vector版本)。

另一方面,MSVC 拒绝该代码,并指出:

<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>>)'

MSVC 拒绝这个是否不正确?我该如何解决这个问题? Godbolt Link

注意:我可以使用sql.operator<<( temp );,但当有很多东西被连接在一起时,这并不好。

c++
  • 2 2 个回答
  • 101 Views

2 个回答

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

    g++ 和 clang++ 是正确的,而 MSVC 是错误的:应该选择成员,operator<<因为它是更专业的模板。

    过载解析的主要步骤是:

    1. 构建一组候选函数。对于运算符表达式 ( [over.match.oper] ),这可以包括:

      • 成员函数 - “vector” 函数模板 1template<class T> MySqlStream & MySqlStream::operator<<(const std::vector<T> &);
      • 非成员函数 - “模板” 函数模板 2 template<class T> MySqlStream & ::operator<<(MySqlStream &, const T &);,以及一些std::operator<<函数模板
      • 内置候选 - 表示整数左移
      • 重写候选人 - 在这种情况下没有
    2. 对于集合中的每个非静态成员函数 [template],将其视为具有额外的第一个参数,其类型是对类类型的引用 ( [over.match.funcs.general]/2 )。相应的参数基于.或左侧的表达式->,这可能涉及隐式this->。在此示例中,

      • 函数模板 1template<class T> MySqlStream & MySqlStream::operator<<(MySqlStream &, const std::vector<T> &);
    3. 对于集合中的每个函数模板,进行模板参数推导以获得具体的函数类型([over.match.funcs.general]/8,[temp.deduct])。所有 的推导均失败std::operator<<,因此它们被抛出集合。我们得到:

      • 功能 1MySqlStream & operator<<(MySqlStream &, const std::vector<double> &);
      • 功能 2MySqlStream & operator<<(MySqlStream &, const std::vector<double> &);
    4. 检查每个函数是否“可行”([over.match.viable])。粗略地说,这意味着根据参数类型、参数类型和约束来调用函数是有效的,但忽略一些其他规则,如私有或受保护的访问和已删除的函数。两个MySqlStream函数都是可行的。内置候选函数不可行,并从集合中丢弃。此时,MySqlStream只剩下这两个函数。

    5. 确定最佳可行函数 ( [over.match.best] )。这是本例中的关键部分。

    由于此时函数类型相同,因此有关隐式转换序列的大部分文本都无法确定更好的函数。重要的规则是[over.match.best.general]/(2.5):

    ... 一个可行函数 F 1被定义为比另一个可行函数 F 2更好的函数,如果 ...,那么

    • ...,或者如果不是这样,
    • F1和F2是函数模板特化,并且 的函数模板比​​ 的函数模板F1更专业化,根据[temp.func.order]F2中描述的偏序规则,如果不是这样,
    • ...

    同样,非静态成员函数模板被认为具有额外的第一个参数,它是对类类型的引用([temp.func.order]/3)。虽然此规则的确切规则与 [over.match.funcs.general]/2 中的先前步骤不同,但在这种情况下,我们再次得到相同的结果

    • 函数模板 1template<class T> MySqlStream & MySqlStream::operator<<(MySqlStream &, const std::vector<T> &);

    [temp.func.order] 或相关的[temp.deduct.partial]中没有其他内容提及非静态成员函数的特殊第一个参数或第一个参数,因此唯一剩下的重要区别是函数参数类型const std::vector<T> &与const T &。为了简化一点,现在让我们看一个处理这些问题的相关示例:

    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);
    }
    

    [temp.deduct.partial] 的要点是为一个模板 F1 发明尽可能通用的模板参数,并查看与结果函数特化完全匹配的调用是否也与另一个原始模板 F2 匹配。如果匹配,则 F1“至少与 F2 一样特化”,这意味着(几乎)任何时候 F1 可行,F2 也同样可行。以两种方式执行此操作,如果只有一种方式成功,则该测试中的 F1 函数更特化。

    因此,首先让我们专门化一下:发明一种与任何其他声明无关的g1类型,并将其替换为:T1T

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

    接下来看看是否g2可以使用完全匹配的参数进行调用。由于此处的参数是左值引用,因此参数应该是左值表达式。

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

    当然,g2推断它是可行的。T至少和一样专业。std::vector<T1>g1g2

    反过来,我们发明一种类型struct T2 {};并将其代入g2:

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

    并且可以g1使用完全匹配的参数来调用吗?

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

    不,该const T2参数不是 的任何特化std::vector,因此 的模板参数推导g1失败。g2至少不像 那样专业化g1。 合起来,这些意味着g1比 更专业化g2。

    类似地,在原始示例中,“vector”成员函数模板 1 是更专业的函数模板和更好的可行函数,并且由表达式的重载解析选择<<。重载解析并不像 MSVC 声称的那样含糊不清。

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

    看起来 gcc 和 clang 是正确的。以下是来自 cppreference 的引用:

    函数模板 - cppreference.com

    • 如果所比较的两个函数模板中只有一个是成员函数,并且该函数模板是某个类的非静态成员A,则将新的第一个参数插入其参数列表中。给定cv作为函数模板的 cv 限定符,并将ref作为函数模板的 ref 限定符(自 C++11 起),则新参数类型为cv A& ,除非ref为&&,或者ref不存在且另一个模板的第一个参数具有右值引用类型,在这种情况下类型为cv A&&(自 C++11 起)。这有助于对运算符进行排序,这些运算符既可作为成员函数查找,也可作为非成员函数查找:
    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
    }
    

    在您的代码成员函数中,第二个参数比自由函数版本const std::vector<T> &更加专业。const T &

    以下是一些实验:

    • 您的原始代码
    • 翻转参数类型(现在首选自由函数) ——这个版本的 MSVC 很有趣。所以这是 MSVC 错误的另一个论点。
    • 1

相关问题

  • 为什么编译器在这里错过矢量化?

  • 使用带有库的 CMake 编译错误[关闭]

  • 每次我尝试运行预制时都会抛出错误

  • 如何在 C++ 中创建类似于 std::byte 的八位字节类型?

  • C++17 中 std::byte 只能按位运算?

Sidebar

Stats

  • 问题 205573
  • 回答 270741
  • 最佳答案 135370
  • 用户 68524
  • 热门
  • 回答
  • Marko Smith

    重新格式化数字,在固定位置插入分隔符

    • 6 个回答
  • Marko Smith

    为什么 C++20 概念会导致循环约束错误,而老式的 SFINAE 不会?

    • 2 个回答
  • Marko Smith

    VScode 自动卸载扩展的问题(Material 主题)

    • 2 个回答
  • Marko Smith

    Vue 3:创建时出错“预期标识符但发现‘导入’”[重复]

    • 1 个回答
  • Marko Smith

    具有指定基础类型但没有枚举器的“枚举类”的用途是什么?

    • 1 个回答
  • Marko Smith

    如何修复未手动导入的模块的 MODULE_NOT_FOUND 错误?

    • 6 个回答
  • Marko Smith

    `(表达式,左值) = 右值` 在 C 或 C++ 中是有效的赋值吗?为什么有些编译器会接受/拒绝它?

    • 3 个回答
  • Marko Smith

    在 C++ 中,一个不执行任何操作的空程序需要 204KB 的堆,但在 C 中则不需要

    • 1 个回答
  • Marko Smith

    PowerBI 目前与 BigQuery 不兼容:Simba 驱动程序与 Windows 更新有关

    • 2 个回答
  • Marko Smith

    AdMob:MobileAds.initialize() - 对于某些设备,“java.lang.Integer 无法转换为 java.lang.String”

    • 1 个回答
  • Martin Hope
    Fantastic Mr Fox msvc std::vector 实现中仅不接受可复制类型 2025-04-23 06:40:49 +0800 CST
  • Martin Hope
    Howard Hinnant 使用 chrono 查找下一个工作日 2025-04-21 08:30:25 +0800 CST
  • Martin Hope
    Fedor 构造函数的成员初始化程序可以包含另一个成员的初始化吗? 2025-04-15 01:01:44 +0800 CST
  • Martin Hope
    Petr Filipský 为什么 C++20 概念会导致循环约束错误,而老式的 SFINAE 不会? 2025-03-23 21:39:40 +0800 CST
  • Martin Hope
    Catskul C++20 是否进行了更改,允许从已知绑定数组“type(&)[N]”转换为未知绑定数组“type(&)[]”? 2025-03-04 06:57:53 +0800 CST
  • Martin Hope
    Stefan Pochmann 为什么 {2,3,10} 和 {x,3,10} (x=2) 的顺序不同? 2025-01-13 23:24:07 +0800 CST
  • Martin Hope
    Chad Feller 在 5.2 版中,bash 条件语句中的 [[ .. ]] 中的分号现在是可选的吗? 2024-10-21 05:50:33 +0800 CST
  • Martin Hope
    Wrench 为什么双破折号 (--) 会导致此 MariaDB 子句评估为 true? 2024-05-05 13:37:20 +0800 CST
  • Martin Hope
    Waket Zheng 为什么 `dict(id=1, **{'id': 2})` 有时会引发 `KeyError: 'id'` 而不是 TypeError? 2024-05-04 14:19:19 +0800 CST
  • Martin Hope
    user924 AdMob:MobileAds.initialize() - 对于某些设备,“java.lang.Integer 无法转换为 java.lang.String” 2024-03-20 03:12:31 +0800 CST

热门标签

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

Explore

  • 主页
  • 问题
    • 最新
    • 热门
  • 标签
  • 帮助

Footer

AskOverflow.Dev

关于我们

  • 关于我们
  • 联系我们

Legal Stuff

  • Privacy Policy

Language

  • Pt
  • Server
  • Unix

© 2023 AskOverflow.DEV All Rights Reserve