为什么在这种情况下重载解析是不明确的?

时间:2015-12-26 12:59:34

标签: c++ sfinae

我已编写此代码以检查类类型是否具有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::*)() = &HasfuncBegin<T>::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};
};

2 个答案:

答案 0 :(得分:3)

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

变量参数函数(...)是零个或多个参数的匹配(即check<T>())。具有默认值的单个参数的函数可以匹配check<T>()

因此有关含糊不清的信息。

你还没有真正描述过你要用这个代码实现的目标(特别是enum的初始化),但是我们会以某种方式期望我们能够解决你想要做的事情。让它编译的明显方法是删除其中一个重载。

但是,除非你描述你真正想要实现的目标,否则没有人可以建议你。像这样的阅读网站不会授予人们思维能力。

答案 1 :(得分:0)

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

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

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

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

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

ex2()电话不正确。没有用于比较转换序列的参数,并且可以在没有参数的情况下调用这两个重载。这与您的代码类似。

看起来您已经厌倦了C ++ 03,所以这是使用article的可能解决方案:

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

this answer