У меня есть следующий код отправки тегов (см. LiveWorkSpace)
#include <iostream>
// traits types
struct A {}; struct B {}; struct C {};
// helpers
void fun_impl(bool, A) { std::cout << "A\n"; }
void fun_impl(bool, B) { std::cout << "B\n"; }
// would like to eliminate this
void fun_impl(bool b, C)
{
if(b)
fun_impl(b, A());
else
fun_impl(b, B());
}
template<typename T>
void fun(bool b, T t)
{
// T = A, B, or C
fun_impl(b, t);
}
int main()
{
fun(true, A()); // "A"fun(false, A()); // "A"fun(true, B()); // "B"fun(false, B()); // "B"fun(true, C()); // "A"fun(false, C()); // "B"}
Однако эта диспетчеризация тега тесно связана с функцией fun
и мне нужно сохранить 3 вспомогательные функции для реализации этой диспетчеризации тегов для каждой функции, которая использует это.
Аргумент вычета не удается: Я попытался абстрагировать fun_impl
в параметр шаблона mixed_dispatch
объект функции, но если я потом передам fun_impl
в качестве аргумента невозможно определить, какая из двух перегрузок должна быть обязательной.
template<typename T>
struct mixed_dispatch
{
template<typename Fun>
void operator()(Fun f, bool b)
{
return f(b, T());
}
};
template<>
struct mixed_dispatch<C>
{
template<typename Fun>
void operator()(Fun f, bool b)
{
if (b)
return f(b, A());
else
return f(b, B());
}
};
template<typename T>
void fun(bool b, T)
{
// T = A, B, or C
mixed_dispatch<T>()(fun_impl, b); // ERROR: Fun cannot be deduced
}
Вопрос: есть ли какой-либо другой способ отделить диспетчеризацию тега от вызываемой функции?
Я открыт для любых предложений, использующих C ++ 11 variadic templates / Boost.Fusion или другое волшебство, которое упрощает мой текущий код (теперь я должен поддерживать 3 вместо 2 вспомогательных функций для каждой функции, которая использует эту конкретную диспетчеризацию, и с более сложная диспетчеризация, количество вспомогательных функций растет еще быстрее).
Чтобы выбрать одну из перегруженных функций, по крайней мере, вы должны указать компилятору типы аргументов целевой функции. Таким образом, мы можем добавить их в качестве аргументов типа класса шаблона mixed_dispatch
template < typename Tag, typename... Args >
class mixed_dispatch {
std::function<void(Tag,Args...)> invoke;
public:
// for overloaded functions
mixed_dispatch( void(&f)(Tag,Args...) ) : invoke( f ) { }
// for function objects
template < typename F >
mixed_dispatch( F&& f ) : invoke( std::forward<F>(f) ) { }
void operator()( Args... args ) const {
invoke( Tag(), args... );
}
};
Сейчас mixed_dispatch
становится оберткой, которая поможет вам пройти Tag
объекты к целевой функции. Как видите, нам нужно изменить сигнатуру целевой функции (немного работает).
void fun_impl(A1, bool) { std::cout << "A1\n"; }
В коде клиента, как fun
:
template< typename T >
void fun( bool b, T )
{
using dispatcher = mixed_dispatch<T,bool>;
dispatcher d = fun_impl;
d( b );
}
Это отвечает на любые ваши вопросы?
// traits types
struct A1 {}; struct B1 {};
struct A2 {}; struct B2 {};
template<class T1, class T2>
struct TypeSelector
{
typedef T1 TTrue;
typedef T2 TFalse;
};
typedef TypeSelector<A1, B1> C1;
typedef TypeSelector<A2, B2> C2;
// helpers
void fun_impl(bool, A1) { std::cout << "A1\n"; }
void fun_impl(bool, B1) { std::cout << "B1\n"; }
void fun_impl(bool, A2) { std::cout << "A2\n"; }
void fun_impl(bool, B2) { std::cout << "B2\n"; }
template<class TSel>
void fun_impl(bool b, TSel) { if(b) fun_impl(b, typename TSel::TTrue()); else fun_impl(b, typename TSel::TFalse()); }
template<typename T>
void fun(bool b, T t)
{
// T = A, B, or C
fun_impl(b, t);
}
int main()
{
fun(true, A1()); // "A1"fun(false, A1()); // "A1"fun(true, B1()); // "B1"fun(false, B1()); // "B1"fun(true, C1()); // "A1"fun(false, C1()); // "B1"fun(true, C2()); // "A2"fun(false, C2()); // "B2"}
Или вы хотите упростить код в другом «измерении»?