2015-12-26 5 views
5

Я написал этот код, чтобы проверить, есть ли тип класса 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); имеет аргумент по умолчанию будет использоваться) и также, как сделать свой код работа?

Edit:

Так вот окончательный рабочий код:

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}; 
}; 
+0

Почему бы не использовать что-то вроде 'имяТипа станд :: условно :: значение, один, два> :: type'? –

+0

Аргументы функции по умолчанию не являются частью сигнатуры функции и не рассматриваются SFINAE. – cpplearner

+0

Возможный дубликат http://stackoverflow.com/q/257288/1639256 или http://stackoverflow.com/q/1966362/1639256 – Oktalist

ответ

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, так что здесь возможное решение с использованием 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

+0

Можете ли вы исправить мой код с помощью резервного механизма, который я пытался использовать? Спасибо) –

+0

Я не совсем понимаю, для чего нужен резерв. Планируете ли вы наследовать от 'HasfuncBegin', чтобы вы получили значение по умолчанию, если у указанного класса нет его? – TartanLlama

+0

BTW комментарий в оригинальном вопросе: «Также вы не делаете SFINAE, потому что нет аргумента шаблона, который выведен» имеет смысл для вас сэр? Спасибо –

3

Причиной неоднозначности является то, что оба (шаблонных) перегрузками check() являются действительными для матчей check<T>(). Вы можете думать, что один из них более важен, чем другой, но правила языка - это то, что они оба одинаково действительны.

Функция переменных аргументов (...) соответствует нулю или более аргументам (то есть check<T>()). Функция с единственным аргументом, который имеет значение по умолчанию, может соответствовать check<T>().

Отсюда сообщение о двусмысленности.

Вы на самом деле не описали то, что вы пытаетесь достичь с помощью этого кода (в частности, инициализация enum), но как-то ожидаем, что мы разработаем то, что вы пытаетесь сделать. Очевидным способом его компиляции было бы удалить одну из перегрузок.

Но, если вы не описываете то, что вы действительно пытаетесь достичь, никто не может вам посоветовать. Чтение сайтов, подобных этому, не дает людям возможности понять.

+0

Что я ' m, пытающийся достичь, написан в самой первой строке вопроса: «Я написал этот код, чтобы проверить, начал ли тип класса.» –

+0

И этого описания недостаточно. Сожалею. – Peter

+0

@Peter Разве это не ясно из кода? Если существует функция 'begin()' member, 'yes' в перечислении должна быть true. В зависимости от разрешения перегрузки выбирается один или другой 'check()', что приводит к другому значению для 'yes' (не работает, но это была идея). –