C++ C++;函数重载中的不明确调用 只是一个好奇心(学术问题:P),考虑下面的代码: struct Y {}; class PassConstY { public: PassConstY(const Y& y) {} }; class PassY { public: PassY(Y& y) {} }; void z(PassConstY y) {} void z(PassY y) {} void h(const Y& y) {} void h(Y& y) {} Y y; const Y cy; h(cy); //OK h(y); //OK z(cy); //OK z(y); //Ambiguity

C++ C++;函数重载中的不明确调用 只是一个好奇心(学术问题:P),考虑下面的代码: struct Y {}; class PassConstY { public: PassConstY(const Y& y) {} }; class PassY { public: PassY(Y& y) {} }; void z(PassConstY y) {} void z(PassY y) {} void h(const Y& y) {} void h(Y& y) {} Y y; const Y cy; h(cy); //OK h(y); //OK z(cy); //OK z(y); //Ambiguity,c++,C++,问题:是否可以编写PassY和PassConstYs.t z和h遵循完全相同的重载规则 如果我删除可变Y的z和h定义,代码仍会编译(即,我也可以调用可变版本的z和h) 我的猜测是否定的,从某种意义上说,我设法只从一个常量Y构造了PassConstY(而不是一个可变Y),这消除了歧义,但第2点注定要失败 澄清: PassY和PassConstY可以根据您的喜好定义(但必须是类,可以是模板),但z和h的定义必须保持不变 当仅定义z和h的“const”版本时,必须编译以下代码: const Y y;

问题:是否可以编写
PassY
PassConstY
s.t

  • z
    h
    遵循完全相同的重载规则
  • 如果我删除可变
    Y
    z
    h
    定义,代码仍会编译(即,我也可以调用可变版本的
    z
    h
  • 我的猜测是否定的,从某种意义上说,我设法只从一个
    常量Y
    构造了
    PassConstY
    (而不是一个可变
    Y
    ),这消除了歧义,但第2点注定要失败


    澄清:

    PassY和PassConstY可以根据您的喜好定义(但必须是类,可以是模板),但z和h的定义必须保持不变

    当仅定义z和h的“const”版本时,必须编译以下代码:

    const Y y;
    z(y); //Calls z(PassConstY y)
    h(y); //Calls h(const Y& y)
    
    Y x;
    z(x); //Calls z(PassConstY y)
    h(x); //Calls h(const Y& y)
    
    Y x;
    z(x); //Calls z(PassY y)
    h(x); //Calls h(Y& y)
    
    const Y y;
    z(y); //Calls z(PassConstY y)
    h(y); //Calls h(const Y& y)
    
    Y x;
    z(x); //Calls z(PassY y)
    h(x); //Calls h(Y& y)
    
    当仅定义z和h的“可变”版本时,必须编译以下代码:

    const Y y;
    z(y); //Calls z(PassConstY y)
    h(y); //Calls h(const Y& y)
    
    Y x;
    z(x); //Calls z(PassConstY y)
    h(x); //Calls h(const Y& y)
    
    Y x;
    z(x); //Calls z(PassY y)
    h(x); //Calls h(Y& y)
    
    const Y y;
    z(y); //Calls z(PassConstY y)
    h(y); //Calls h(const Y& y)
    
    Y x;
    z(x); //Calls z(PassY y)
    h(x); //Calls h(Y& y)
    
    定义h和z的两个版本时,必须编译以下代码:

    const Y y;
    z(y); //Calls z(PassConstY y)
    h(y); //Calls h(const Y& y)
    
    Y x;
    z(x); //Calls z(PassConstY y)
    h(x); //Calls h(const Y& y)
    
    Y x;
    z(x); //Calls z(PassY y)
    h(x); //Calls h(Y& y)
    
    const Y y;
    z(y); //Calls z(PassConstY y)
    h(y); //Calls h(const Y& y)
    
    Y x;
    z(x); //Calls z(PassY y)
    h(x); //Calls h(Y& y)
    
    对不起,如果原来的问题不够清楚


    动机(这是我在Stackoverflow上的另一个问题):

    考虑到下面评论中的“禁用r值绑定”,我想提出一个模板类Ref(将其视为智能引用),我可以在编写函数定义时使用它,如:

    struct X {};
    
    void f(Ref<X> x) {} //Instead of void f(X& x)
    void f(Ref<const X> x) {} //Instead of void f(const X& x)
    
    struct X{};
    void f(Ref x){}//而不是void f(x&x)
    void f(Ref x){}//而不是void f(const x&x)
    
    因此,在调用函数f时(就重载解析/转换而言),我得到了与普通引用版本相同的行为,但Ref从未绑定到右值

    [对于这部分,需要C++0X]

    原因是:

    • 如果SCS 1将引用绑定到常量,SCS 2将引用绑定到非常量,则SCS 1优于SCS 2
    • UCS 1优于UCS 2,当且仅当两者使用相同的转换函数或构造函数时
    对于使用SCS的
    h
    ,如果两个函数都是可行的候选函数(第二次调用),则第一个项目符号将确定胜利者。对于
    z
    ,如果两个函数都是可行的候选函数(第二次调用),则没有比其他UCS更好的UCS,因为两个UCS使用不同的构造函数

    请注意,UCS定义为

    • 一个SCS(如果使用构造函数,将参数转换为构造函数的参数类型;对于转换运算符函数,将参数转换为
      TypeOf(*this)&
    • 调用用户定义的转换(构造函数或转换运算符函数)
    • 另一个SCS(将转换结果转换为最终目标类型)
    您无法为“const Y to Y&”格式化SCS,因此第二个
    h
    函数的UCS不存在第一个SCS,因此它不是可行的候选者。但是,对
    h
    的第二次调用并非如此,因此会导致上述歧义


    我缩短了一些术语:

    UCS:用户定义的转换顺序
    标准转换序列


    我的猜测是否定的,从某种意义上说,我设法让PassConstY仅从一个consty(而不是一个可变的Y)构造,这消除了歧义,但第2点注定要失败

    如果我理解正确的话,你的这种怀疑是不对的。使用当前的定义,您可以删除
    h
    z
    的可变版本,您的代码将可以正常编译。有一个SCS用于
    Y
    const Y&


    当然,您可以将PassY和PassConstY重写为

    typedef Y &PassY;
    typedef Y const& PassConstY;
    

    除此之外,我不知道你说“重写”是什么意思。如果您只想更改类主体,那么重载解析肯定会有所不同

    我不确定这是否对您有帮助,但您可以为使用模板的
    z
    编写包装器:

    template <typename T>
    struct TypeFor {
        typedef PassY type;
    };
    
    template <typename T>
    struct TypeFor<const T> {
        typedef PassConstY type;
    };
    
    template <typename T>
    void  wrap_z(T& y) { z(static_cast<typename TypeFor<T>::type>(y)); }
    
    模板
    结构类型{
    typedef-PassY型;
    };
    模板
    结构类型{
    typedef PassConstY类型;
    };
    模板
    空包z(T&y){z(静态cast(y));}
    
    现在,根据调用参数类型,
    T
    将变为
    Y
    const Y
    ,并将使用正确的显式转换

    这使用了结构(而不是函数)可以部分特殊化的事实,我们在这里针对
    T

    z(y)的不同常量所做的工作是不明确的,因为在这两者之间有一个隐式转换,它不知道是哪一个

    如果对PassY和PassConstY使用显式构造函数,那么两个z都将失败

    还可以使用隐式构造函数传递模板:

    template< typename T > 
    class Pass
    {
       Pass( T& t ) {}
    };
    
    typedef Pass<Y> PassY;
    typedef Pass<const Y> PassConstY;
    
    模板
    班级通行证
    {
    通过(T&T){}
    };
    typedef PassY;
    typedef PassConstY;
    
    我仍然不确定这是否能解决您的问题,但您也可以将z作为模板函数:

    template< typename T> void z( Pass<T> t );
    
    templatevoid z(通过T);
    
    虽然在实际代码中,我会避免隐式转换并使用函数

    template<typename T> Pass<T> pass(T& t) { return Pass<T>(t); }
    
    模板传递(T&T){返回传递(T);}
    
    以上是部分专业化,您还可以定义

    template< typename T> void z( T& t) { z( pass(t) ); }
    
    templatevoid z(T&T){z(pass(T));}
    

    它应该可以工作。

    我想要实现的是编译代码(对于PassY和PassConstY的某些定义,它们的构造函数可以根据您的喜好进行更改),这两种情况都是:1)仅存在h和z的const版本,2)存在h和z的const和可变版本。但是PassY和PassConstY必须是类(而不是typedef),因为我必须对它们执行其他操作(禁用与rvalues:P的绑定)。谢谢你