2014-10-03 1 views
2

Предположим, пользователь определяет некоторое подмножество следующих функций:Выберите наилучшую имеющуюся функцию с помощью тегов наследования

void f(int) {} 
void g(int) {} 
void h(int) {} 
// ... 

Ваша задача состоит в том, чтобы написать функцию call_best(int) которая вызывает первую функцию из приведенного выше списка, который объявлен (вы тогда можно предположить, что он также определен). Как ты это делаешь?

+4

Просто любопытно, что случай использования этого? –

+0

Все эти функции определены. –

+0

@NicolasHolthaus: Я думаю, что OP хочет сделать что-то вроде [call-a-free-function-вместо-of-method-if-it-doesnt-exist] (http://stackoverflow.com/questions/21441803/ call-a-free-function-in-a-method-if-it-doesnt-exist) – Jarod42

ответ

4

Сначала мы определяем класс приоритета.

template<unsigned P> struct priority : priority<P-1> {}; 
template<> struct priority<0> {}; 

Он может быть использован для получения общего порядка на функции следующим образом:

template<class Int> auto call_best(Int i, priority<2>) -> decltype(f(i)) { return f(i); } 
template<class Int> auto call_best(Int i, priority<1>) -> decltype(g(i)) { return g(i); } 
template<class Int> auto call_best(Int i, priority<0>) -> decltype(h(i)) { return h(i); } 

void call_best(int i) { call_best(i, priority<2>{}); } 

Int параметр шаблона и decltype() убедитесь, что только определенные функции конкурируют за то, что называется (ключевое слово SFINAE). Класс тегов priority позволяет нам выбрать лучший из них.

Обратите внимание, что часть SFINAE работает только в том случае, если у вас есть как минимум один параметр, на котором вы можете создать шаблон. Если у кого-то есть идея о том, как этого избежать, скажите, пожалуйста.

+0

Достаточно просто объявить одну из функций. Затем он будет компилироваться, но не будет связан. Мне тоже интересно, что это такое. – jrok

+0

Насколько я знаю, он компилируется и запускается, если вы определяете хотя бы одну функцию, см. Здесь: http://ideone.com/Jk4Dbo Конечно, он не компилируется, если вы не определяете ни одну из функций, но это предназначенное поведение. В лучшем случае можно применить некоторую косметику к сообщению об ошибке, которое получает в этом случае. – gTcV

+0

Не будет ошибкой, если перегружены 'f',' g' или 'h'? –

0

Ваш код будет работать, если вы согласны с предоставлением своим функциям выведенного типа возврата (т. Е. auto/decltype(auto)). Это можно сделать только в C++ 14, в котором функция с выведенным типом возвращаемого значения не может использоваться до тех пор, пока она не будет определена (даже в неоцененном операнде), что приведет к другому отказу замены. Вот ваш пример, который работает в clang 3.5, но, к сожалению, не в g ++ 4.9.

template<unsigned P> struct priority : priority<P-1> {}; 
template<> struct priority<0> {}; 

// ******** 
auto f(int); 
auto g(int); 
auto h(int) { std::cout << "h()"; } 
// ******** 

template<class Int> auto call_best(Int i, priority<2>) -> decltype(f(i)) { return f(i); } 
template<class Int> auto call_best(Int i, priority<1>) -> decltype(g(i)) { return g(i); } 
template<class Int> auto call_best(Int i, priority<0>) -> decltype(h(i)) { return h(i); } 

void call_best(int i) { call_best(i, priority<2>{}); } 

int main() 
{ 
    call_best(0); // calls h() 
} 

clang 3.5 g++ 4.9