【问题标题】:Why is overload resolution is ambiguous in this case?为什么在这种情况下重载决议是模棱两可的?
【发布时间】:2015-12-26 12:59:33
【问题描述】:

我编写了这段代码来检查一个类类型是否有begin 函数。

struct foo //a simple type to check
{
    int begin(){ return 0;}
};

struct Fallback
{
    int begin(){ return 0;}
};

template<typename T>
struct HasfuncBegin : T,Fallback
{
    typedef char one;
    typedef int two;

    template<typename X>
    static one check(int (X::*)() = &HasfuncBegin<T>::begin);
    template<typename X>
    static two check(...);

    enum :bool {yes = sizeof(check<T>())==1, no= !yes};
};

int main()
{
    std::cout<< HasfuncBegin<foo>::yes;
    return 0;
}

这会产生错误:

error: call of overloaded 'check()' is ambiguous
     enum {yes = sizeof(check<T>())==1, no= !yes};
                                ^
C:\XXX\main.cpp:24:16: note: candidate: static HasfuncBegin<T>::one HasfuncBegin<T>::check(int (X::*)()) [with X = foo; T = foo; HasfuncBegin<T>::one = char]
     static one check(int (X::*)() = &HasfuncBegin<T>::begin);
                ^
C:\XXX\main.cpp:26:16: note: candidate: static HasfuncBegin<T>::two HasfuncBegin<T>::check(...) [with X = foo; T = foo; HasfuncBegin<T>::two = int]
     static two check(...);


        ^

谁能解释一下为什么调用不明确(尽管第一个检查函数签名one check(int (X::*)() = &amp;HasfuncBegin&lt;T&gt;::begin); 有默认参数要使用)以及如何让我的代码工作

编辑:

所以这是最终的工作代码:

struct foo
{
    int begin(){ return 0;}
};

struct Fallback
{
    int begin(){ return 0;}
};

template<typename T, T ptr> struct dummy{};

template<typename T>
struct HasfuncBegin : T,Fallback
{
    typedef char one;
    typedef int two;


    template<typename X>
    static one check(dummy<int (X::*)(),&HasfuncBegin<X>::begin>*);
// even this won't work, so replace above statement with below commented one
// static one check(dummy<decltype(&HasfuncBegin<X>::begin),&HasfuncBegin<X>::begin>*); 
    template<typename X>
    static two check(...);

    enum {yes = sizeof(check<T>(0))==1, no= !yes};
};

【问题讨论】:

  • 为什么不使用 typename std::conditional&lt;std::is_same&lt;decltype(&amp;X::begin),int (X::*)()&gt;::value, one, two&gt;::type 之类的东西?
  • 默认函数参数不是函数签名的一部分,SFINAE 不考虑。
  • 另外你没有做 SFINAE 因为没有模板参数被推导出来

标签: c++ sfinae


【解决方案1】:

模棱两可的原因是check() 的两个(模板化)重载都是check&lt;T&gt;() 的有效匹配项。你可能认为一个比另一个更有效,但语言的规则是它们都同样有效。

可变参数函数(...)匹配零个或多个参数(即check&lt;T&gt;())。具有单个参数且具有默认值的函数可以匹配 check&lt;T&gt;()

因此出现了关于歧义的信息。

您实际上并没有描述您要使用此代码实现的目标(特别是 enum 的初始化),但不知何故期待我们会解决您正在尝试做的事情。让它编译的明显方法是删除其中一个重载。

但是,除非你描述你真正想要达到的目标,否则没有人可以给你建议。像这样阅读网站并不能赋予人们读心的能力。

【讨论】:

  • 我想要达到的目标写在问题的第一行:I've written this code to check if a class type have begin function.
  • 这个描述是不充分的。对不起。
  • @Peter 从代码中不是很清楚吗?如果存在begin() 成员函数,则枚举中的yes 应该为真。根据重载解决方案,选择一个或另一个 check(),从而导致 yes 的值不同(不起作用,但这就是想法)。
【解决方案2】:

调用不明确,因为重载选择基于从调用参数到函数参数的转换序列。规则有点复杂,在这里无法完整解释,但请考虑以下两个示例:

void ex1(int) {} //v1
void ex1(...) {} //v2

void ex2(int = 1) {}    //v1
void ex2(...) {} //v2

int main() {
   ex1(1);
   ex2();
}

ex1(1) 调用格式正确。有一个参数比v2 具有更好的到v1 的隐式转换序列(精确匹配与省略号转换)。

ex2() 调用格式错误。没有用于比较转换序列的参数,并且两个重载都可以在没有参数的情况下调用。这类似于您的代码。


看起来你被 C++03 卡住了,所以这里有一个使用 this answer 的可能解决方案:

template<typename T>                               
struct HasfuncBegin {                                                       
    typedef char yes[1];                                            
    typedef char no [2];                                            
    template <typename U, U> struct type_check;                     
    template <typename _1> static yes &chk(type_check<int (T::*)(), &_1::begin > *); 
    template <typename   > static no  &chk(...);                    
    static bool const value = sizeof(chk<T>(0)) == sizeof(yes);     
};

Live Demo

【讨论】:

  • 您可以使用我尝试使用的回退机制更正我的代码吗?谢谢)
  • 我不完全理解后备的用途。您是否打算从 HasfuncBegin 继承,以便在您指定的类没有时获得默认值?
  • 顺便说一句,原始问题中的评论:“您也没有做 SFINAE,因为没有推断出模板参数”对您来说有意义吗?谢谢
  • 是的,这是因为您依赖于默认参数,而这并不适用。
  • 但是看例子cpp.sh/4jix,没有进行参数推导,我明确指定了所有模板参数。这证明不存在论证推论。尽管您指定的参数转换/绑定正在进行,这在我的原始代码中产生了错误。
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2020-08-25
  • 1970-01-01
  • 2013-04-23
相关资源
最近更新 更多