2010-09-30 3 views
2

В шаблонах, как показано ниже, я хотел бы, чтобы вызов Run(&Base::foo) преуспеть, без необходимости дважды указывать тип Base (как это сделано при компиляции вызова Run<Base>(&Base::foo)). Могу ли я получить это? Возможно, без добавления тонны заголовков Boost?Как вывести тип класса из типа метода в шаблонах C++?

С предоставленной кодой, я получаю ошибку:

prog.cpp:26: error: no matching function for call to ‘Run(bool (Base::*)())’ 

(вы можете возиться с сниппета в http://ideone.com/8NZkq):

#include <iostream> 

class Base { 
public: 
    bool foo() { return true; } 
}; 

Base* x; 

template<typename T> 
struct Traits { 
    typedef bool (T::*BoolMethodPtr)(); 
}; 

template<typename T> 
void Run(typename Traits<T>::BoolMethodPtr check) { 
    T* y = dynamic_cast<T*>(x); 
    std::cout << (y->*check)(); 
} 

int main() { 
    Base y; 
    x = &y; 
    Run<Base>(&Base::foo); 
    Run(&Base::foo); // why error? 
} 
+1

Вы знаете, компиляторы не работают автоматически, как вы хотите, чтобы они ... – ianmac45

+1

Что с трудными вопросами на этом сегодня утром? Обычно это куча «как вы форматируете шестнадцатеричное число» или «что такое метод const». Сегодня утром мне действительно нужно думать. :) –

+1

@ian: но делаем! – Potatoswatter

ответ

9

T в Traits<T>::BoolMethodPtr находится в не выводятся контекст, так что компилятор не будет выводить автоматически из вызова, что должно быть типа T. Это потому, что может быть такой код:

template<typename T> 
struct Traits { 
    typedef bool (T::*BoolMethodPtr)(); 
}; 

template<> 
struct Traits<int> { 
    typedef bool (Base::*BoolMethodPtr)(); 
}; 

Run(&Base::foo); /* What should T be deduced to? Base and int are both equally possible */ 

Если вы можете обойтись без Traits<T> класса, вы можете написать Run как:

template<class Class> 
void Run(bool (Class::*check)()) { 
    Class* y = dynamic_cast<Class*>(x); 
    std::cout << (y->*check)(); 
} 

В этом контексте Class можно вывести в виду Base

+1

d'oh! Ваш ответ лучше моего. – Potatoswatter

+1

Конечно, я могу обойтись без! Aargh, так что вы говорите, что это выполнимо, и я слишком усложнен; LOL, смешно в жизни :) Большое спасибо за решение; в отношении объяснения, похоже, мне придется подойти к нему через день, после хорошего ночного сна;) Престижность всем в этой теме, вы получите свои +1, все ответы очень интересны, каждый приносит часть головоломки – akavel

2

Я думаю, что это не выводятся контекст.

$ 14.8.2.5/5 «В не-выведены контекстах являются: -. Вложенным именем-спецификатор из типа, который был определен с помощью квалифицированного идентификатор»

Я думаю, что это цитата, которая применяется в этом случае. Но некоторые боги-шаблоны должны ратифицировать мое понимание.

+0

Вы запомнили стандарт или что?':)' –

2

Когда компилятор пытается сопоставить аргумент шаблона, он учитывает только тип первичного класса. Другими словами, когда он встречает выражение:

Run(&Base::foo); 

... и он пытается выяснить параметр шаблона для Run, он рассматривает только тип foo сам по себе, и не учитывает то, что класс foo является часть.

EDIT:

И тип foo является bool(Base::*)(void), но то, что вы хотите, чтобы компилятор найти только Base

+0

В этом контексте компилятор может excullu вычитать класс из bool (Class :: *)(), см. Ответ Барта. –

4

Чтобы выбрать обособленно тип, любой тип, использовать частичную специализацию. Частичной специализации шаблона не существует, поэтому вам нужно будет непосредственно параметризовать функцию по типу аргумента и получить тип класса внутри.

template< typename T > 
struct get_host_class; // most types are not ptmfs: don't implement this 

template< typename C > 
struct get_host_class< bool (C::*)() > { // implement partial specialization 
    typedef C host; 
    typedef void sfinae; // disallow function for non ptmf arguments 
}; 

template< typename T > 
typename get_host_class<T>::sfinae Run(T check) { 
    typedef T BoolMethodPtr; // or something 
    typedef typename get_host_class<T>::host host; 
} 
+0

Ум ... не шаблонный эксперт здесь, так что не могли бы вы рассказать о том, что расширяет 'sfinae' и 'ptmf'? для меня, по крайней мере, это упростило бы чтение :) – akavel

+1

@aka: sfinae = Ошибка замещения - это не ошибка, трюк для (помимо всего прочего) эмуляции частичной специализации функций. ptmf = Pointer To Member Function, тип типа, вызывающий вас, и многие другие, горе. – Potatoswatter

+0

Хорошо, спасибо. Хотя в моем личном деле (довольно просто), я бы предпочел для ясности Ctrl-C и Ctrl-V или #define, это может быть полезно в некоторых более сложных. +1 также для кучки довольно интересных методов метапрограммирования в фрагменте :) – akavel