Я отказываюсь сказать, что я не могу понять это, но я не могу понять это. Я погуглил и искал Переполнение стека и подошел пустой.
Краткий обзор, и возможно чрезмерно неопределенная форма вопроса, как я могу использовать шаблон черт для инстанцирования функций членства? [Обновление: Я использовал неправильный термин здесь. Это должны быть "политики", а не "черты". Черты описывают существующие классы. Политики предписывают синтетические классы.] Вопрос подошел при модернизации ряда многомерных функциональных оптимизаторов, которые я записал больше чем 10 лет назад.
Оптимизаторы все действуют путем выбора прямолинейного пути через пространство параметров далеко от текущей лучшей точки ("обновление"), затем нахождения лучшей точки на той строке ("поиск строки"), затем тестирования на "сделанное" условие, и если не сделанный, выполняя итерации.
Существуют различные методы для того, чтобы сделать обновление, поиск строки, и очевидно для сделанного теста и других вещей. Смешивание и подгонка. Различные формулы обновления требуют различных данных переменной состояния. Например, обновление LMQN требует вектора, и обновление BFGS требует матрицы. Если оценка градиентов является дешевой, поиск строки должен сделать так. В противном случае это должно использовать функциональные оценки только. Некоторые методы требуют более точных поисков строки, чем другие. Это - просто некоторые примеры.
Исходная версия инстанцирует нескольких из комбинаций посредством виртуальных функций. Некоторые черты выбраны путем установки битов режима, которые тестируются во времени выполнения.Фу. Это было бы тривиально для определения черт с #define и функций членства с #ifdef и макросы. Но это так двадцать лет назад. Это прослушивает меня, что я не могу выяснить удар свиста современный путь.
Если бы была только одна черта, которая варьировалась, то я мог использовать любопытно повторяющийся шаблонный шаблон. Но я не вижу способа расширить это до произвольных комбинаций черт.
Я пытался делать его использование boost::enable_if
, и т.д. Специализированная информация состояния была легка. Мне удалось сделать функции, но только путем обращения к недругу внешних функций, которые имеют this
- указатель в качестве параметра. Я даже не выяснил, как завести друзей функций, а тем более функции членства. Компилятор (VC ++ 2008) всегда жаловался, что вещи не соответствовали. Я вопил бы, "SFINAE, Вы идиот!" но идиот - вероятно, я.
Возможно, отправка тега является ключом. Я не добрался очень глубоко в это.
Конечно, это возможно, правильно? Если так, что такое лучшая практика?
ОБНОВЛЕНИЕ: вот другая попытка при объяснении его. Я хочу, чтобы пользователь смог заполнить порядок (декларация) для пользовательского оптимизатора, чего-то как удаление китайского меню - один из столбца A, один из столбца B, и т.д. Официант, из столбца A (updaters), у меня будет обновление BFGS с соусом Cholesky-decompositon. Из столбца B (искатели строки) у меня будет поиск строки кубичной интерполяции с ЭТА 0,4 и ро 1e-4. И т.д...
ОБНОВЛЕНИЕ: Хорошо, хорошо. Вот проигрывание - вокруг этого я сделал. Я предлагаю его неохотно, потому что я подозреваю, что это - абсолютно заблуждающийся подход. Это работает хорошо под vc ++ 2008.
#include <boost/utility.hpp>
#include <boost/type_traits/integral_constant.hpp>
namespace dj {
struct CBFGS {
void bar() {printf("CBFGS::bar %d\n", data);}
CBFGS(): data(1234){}
int data;
};
template<class T>
struct is_CBFGS: boost::false_type{};
template<>
struct is_CBFGS<CBFGS>: boost::true_type{};
struct LMQN {LMQN(): data(54.321){}
void bar() {printf("LMQN::bar %lf\n", data);}
double data;
};
template<class T>
struct is_LMQN: boost::false_type{};
template<>
struct is_LMQN<LMQN> : boost::true_type{};
// "Order form"
struct default_optimizer_traits {
typedef CBFGS update_type; // Selection from column A - updaters
};
template<class traits> class Optimizer;
template<class traits>
void foo(typename boost::enable_if<is_LMQN<typename traits::update_type>,
Optimizer<traits> >::type& self)
{
printf(" LMQN %lf\n", self.data);
}
template<class traits>
void foo(typename boost::enable_if<is_CBFGS<typename traits::update_type>,
Optimizer<traits> >::type& self)
{
printf("CBFGS %d\n", self.data);
}
template<class traits = default_optimizer_traits>
class Optimizer{
friend typename traits::update_type;
//friend void dj::foo<traits>(typename Optimizer<traits> & self); // How?
public:
//void foo(void); // How???
void foo() {
dj::foo<traits>(*this);
}
void bar() {
data.bar();
}
//protected: // How?
typedef typename traits::update_type update_type;
update_type data;
};
} // namespace dj
int main() {
dj::Optimizer<> opt;
opt.foo();
opt.bar();
std::getchar();
return 0;
}
Я считаю, что специализация шаблонов - это шаг в правильном направлении. Это не работает с функциями, поэтому я переключился на классы. Я изменил его так, чтобы он изменял данные. Я не очень продан в защищенных членах и в друзьях. Защищенные члены без наследования - это запах. Сделайте его общедоступным или предоставьте средства доступа и сделайте его частным.
template <typename>
struct foo;
template <>
struct foo<LMQN>
{
template <typename OptimizerType>
void func(OptimizerType& that)
{
printf(" LMQN %lf\n", that.data.data);
that.data.data = 3.14;
}
};
template <>
struct foo<CBFGS>
{
template <typename OptimizerType>
void func(OptimizerType& that)
{
printf(" CBFGS %lf\n", that.data.data);
}
};
template<class traits = default_optimizer_traits>
class Optimizer{
public:
typedef typename traits::update_type update_type;
void foo() {
dj::foo<typename traits::update_type>().func(*this);
}
void bar() {
data.bar();
}
update_type data;
};
Было бы тривиально определить черты с # define и функциями-членами с # ifdef и макросами. Но это было так двадцать лет назад.
Хотя, возможно, стоит изучить новые методы, макросы часто являются самым простым способом делать что-то, и их не следует отбрасывать как инструмент только потому, что они «старые». Если вы посмотрите на MPL in boost и книгу по TMP, вы найдете много использования препроцессора.
Вы используете enable_if
несколько странно. Я видел, что он использовал его только двумя способами:
Использование его для реального параметра может вызвать хаос.
В любом случае, определенно можно использовать его для функций-членов:
template<class traits = default_optimizer_traits>
class Optimizer{
typedef typename traits::update_type update_type;
public:
typename boost::enable_if< is_LQMN<update_type> >::type
foo()
{
// printf is unsafe, prefer the C++ way ;)
std::cout << "LQMN: " << data << std::endl;
}
typename boost::enable_if< is_CBFGS<update_type> >::type
foo()
{
std::cout << "CBFGS: " << data << std::endl;
}
private:
update_type data;
};
Обратите внимание, что по умолчанию enable_if
возвращает void
, что в большинстве случаев идеально подходит в качестве возвращаемого типа. Синтаксис «параметра» обычно зарезервирован для случаев конструктора, потому что тогда в вашем распоряжении нет возвращаемого типа, но в целом предпочитают использовать возвращаемый тип, чтобы он не мешал разрешению перегрузки.
РЕДАКТИРОВАТЬ :
Предыдущее решение не работает, как указано в комментариях. Я не смог найти альтернативы, используя enable_if
, только "простой" способ перегрузки:
namespace detail
{
void foo_impl(const LMQN& data)
{
std::cout << "LMQN: " << data.data << std::endl;
}
void foo_impl(const CBFGS& data)
{
std::cout << "CBFGS: " << data.data << std::endl;
}
} // namespace detail
template<class traits = default_optimizer_traits>
class Optimizer{
typedef typename traits::update_type update_type;
public:
void foo() { detail::foo_impl(data); }
private:
update_type data;
};
Это не enable_if
, но он выполняет свою работу, не открывая внутренности Optimizer
каждому. KISS?
Вот что придумал я (ОП). Можете ли вы сделать его круче?
Основной класс шаблона оптимизатора наследует классы реализации политики. Это дает этим классам доступ к необходимым им защищенным членам оптимизатора. Другой класс шаблона оптимизатора разделяет манифест на составные части и создает экземпляр основного шаблона оптимизатора.
#include <iostream>
#include <cstdio>
using std::cout;
using std::endl;
namespace dj {
// An updater.
struct CBFGS {
CBFGS(int &protect_)
: protect(protect_)
{}
void update () {
cout << "CBFGS " << protect << endl;
}
// Peek at optimizer's protected data
int &protect;
};
// Another updater
struct LMQN {
LMQN(int &protect_)
: protect(protect_)
{}
void update () {
cout << "LMQN " << protect << endl;
}
// Peek at optimizer's protected data
int &protect;
};
// A line-searcher
struct cubic_line_search {
cubic_line_search (int &protect2_)
: protect2(protect2_)
{}
void line_search() {
cout << "cubic_line_search " << protect2 << endl;
}
// Peek at optimizer's protected data
int &protect2;
};
struct default_search_policies {
typedef CBFGS update_type;
typedef cubic_line_search line_search_type;
};
template<class Update, class LineSearch>
class Opt_base: Update, LineSearch
{
public:
Opt_base()
: protect(987654321)
, protect2(123456789)
, Update(protect)
, LineSearch(protect2)
{}
void minimize() {
update();
line_search();
}
protected:
int protect;
int protect2;
};
template<class Search_Policies=default_search_policies>
class Optimizer:
public Opt_base<typename Search_Policies::update_type
, typename Search_Policies::line_search_type
>
{};
} // namespace dj
int main() {
dj::Optimizer<> opt; // Use default search policies
opt.minimize();
struct my_search_policies {
typedef dj::LMQN update_type;
typedef dj::cubic_line_search line_search_type;
};
dj::Optimizer<my_search_policies> opt2;
opt2.minimize();
std::getchar();
return 0;
}
Простым решением может быть использование пересылки на основе тегов, например что-то вроде этого:
template<class traits>
void foo(Optimizer<traits>& self, const LMQN&) {
printf(" LMQN %lf\n", self.data.data);
}
template<class traits>
void foo(Optimizer<traits>& self, const CBFGS&) {
printf("CBFGS %d\n", self.data.data);
}
template<class traits = default_optimizer_traits>
class Optimizer {
friend class traits::update_type;
friend void dj::foo<traits>(Optimizer<traits>& self,
const typename traits::update_type&);
public:
void foo() {
dj::foo<traits>(*this, typename traits::update_type());
}
void bar() {
data.bar();
}
protected:
typedef typename traits::update_type update_type;
update_type data;
};
Или, если вы хотите удобно сгруппировать несколько функций вместе по разным признакам, возможно что-нибудь вроде этого:
template<class traits, class updater=typename traits::update_type>
struct OptimizerImpl;
template<class traits>
struct OptimizerImpl<traits, LMQN> {
static void foo(Optimizer<traits>& self) {
printf(" LMQN %lf\n", self.data.data);
}
};
template<class traits>
struct OptimizerImpl<traits, CBFGS> {
static void foo(Optimizer<traits>& self) {
printf("CBFGS %d\n", self.data.data);
}
};
template<class traits = default_optimizer_traits>
class Optimizer{
friend class traits::update_type;
friend struct OptimizerImpl<traits>;
public:
void foo() {
OptimizerImpl<traits>::foo(*this);
}
// ...
};