2015-04-14 2 views
7

У меня есть код, похожий ниже.Предварительный вариант посетителя с дополнительным параметром

typedef uint32_t IntType; 
typedef IntType IntValue; 
typedef boost::variant<IntValue, std::string> MsgValue; 

MsgValue v; 

Вместо того, чтобы сказать это,

IntValue value = boost::apply_visitor(d_string_int_visitor(), v); 

Я хотел бы передать дополнительный параметр, как это: Но оператор() дает ошибку компиляции.

//This gives an error since the overload below doesn't work. 
IntValue value = boost::apply_visitor(d_string_int_visitor(), v, anotherStr); 

class d_string_int_visitor : public boost::static_visitor<IntType> 
{ 
public: 
    inline IntType operator()(IntType i) const 
    { 
     return i; 
    } 

    inline IntValue operator()(const std::string& str) const noexcept 
    { 
     // code in here 
    } 

    //I want this, but compiler error. 
    inline IntValue operator()(const std::string& str, const std::string s) const noexcept 
    { 
     // code in here 
    } 
}; 
+0

Что вы хотите делать с 'anotherStr'? – P0W

+1

Почему вы не передаете 'anotherStr' конструктору' d_string_int_visitor', а затем используете его? – Nawaz

+0

Решения Preatorian и Yakk не являются общими; они работают только потому, что тип дополнительного параметра является одним из ограниченных типов. Это так называемое двоичное (многократное) посещение. Если дополнительный тип не был ограниченным типом, единственным решением было бы использовать конструктор и переменную-член, как указано Nawaz. – wmamrak

ответ

7

Вы можете связать дополнительный аргумент string посетителю с помощью std::bind. Сначала добавьте параметр std::string во все перегрузки посетителя operator().

class d_string_int_visitor : public boost::static_visitor<IntType> 
{ 
public: 
    inline IntType operator()(IntType i, const std::string& s) const 
    { 
     return i; 
    } 

    inline IntValue operator()(const std::string& str, const std::string& s) const noexcept 
    { 
     // code in here 
     return 0; 
    } 
}; 

Теперь создать посетителя, к которым вы связали второй string аргумент.

auto bound_visitor = std::bind(d_string_int_visitor(), std::placeholders::_1, "Hello World!"); 
boost::apply_visitor(bound_visitor, v); 

Live demo

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

+0

Спасибо Nawaz, Praetorian, у меня было слепое место, по какой-то причине, чтобы передать его d_string_int_visitor. Но я действительно рад, поскольку это решение фактически расширило мое понимание связывания boost. – Ivan

+0

Зачем вам использовать 'std :: bind' в C++ 14 для этого? – Yakk

+0

@Yakk Habit Я думаю (я все еще пишу C++ 03 на работе). Но, IMHO, это один из тех редких случаев, когда решение лямбда не кажется намного более приемлемым по сравнению с решением 'bind'. – Praetorian

3
typedef uint32_t IntType; 
typedef IntType IntValue; 
typedef boost::variant<IntValue, std::string> MsgValue; 

MsgValue v; 

IntValue value = boost::apply_visitor([&](auto&& one){ 
    return d_string_int_visitor{}(decltype(one)(one), anotherStr); 
}, v); 

предполагая каждую перегрузку d_string_int_visitor может обрабатывать дополнительный параметр.

В качестве бонуса, вы даже можете сделать прочь с классом оберточного, если вы хотите:

IntValue to_int_value(IntValue v, std::string const& format) { return v; } 
IntValue to_int_value(std::string const& str, std::string const& format); 

IntValue value = boost::apply_visitor([&](auto&& one){ 
    return to_int_value(decltype(one)(one), anotherStr); 
}, v); 

где мы создаем анонимную лямбду, которая перенаправляет к традиционному набору функциональных перегрузок.

auto&& one и decltype(one)(one) - это метод идеальной пересылки из лямбда (C++ 14). Вы можете заменить второй на std::forward<decltype(one)>(one), но я считаю, что короткая версия читаема. В отличие от std::forward, он делает «неправильную» вещь со значениями типов, но мы знаем, что one является ссылкой на значение l или r.

+0

Обратите внимание, что для этого требуется хотя бы Boost 1.58. –

Смежные вопросы