2017-02-23 25 views
4

Мне интересно, знает ли кто-нибудь об осуществлении (или попытке реализации) этого.Кто-нибудь пытался реализовать Haskell-подобный оператор-заявление в C++?

В Haskell

Haskell есть выражение, которое выполняет случай различие с помощью сопоставления с образцом:

let a = [1,2,3] in 
let is_empty = case a of 
     [] -> True 
     (_:_) -> False in 
-- Note: is_empty == False 
... 

В C++

C++ разрешение перегрузки делает что-то подобное сопоставление с образцом. Всегда можно определить набор перегрузок для разграничения типов по типам, но во многих случаях было бы удобнее использовать выражения.

я могу видеть, как это могло бы почти работы в C++ 14:

template<typename Head, typename Tail> 
using Cons = boost::tuples::cons<Head, Tail>; 
using Nil = boost::tuples::null_type; 

template<typename Tuple> 
bool is_empty(Tuple const & tuple) 
{ 
    return case_of(tuple)(
     [](Cons<auto,auto> const &) { return false; } 
    , [](Nil) { return true; } 
    ); 
} 

Но я не думаю, что Cons<auto,auto> (или чего-то подобного) действует в любой версии C++. У нас есть общие лямбды, но в этом случае я хочу сделать вывод аргумента шаблона в лямбда.

Итак, есть ли что-нибудь подобное этому случаю в дикой природе? Или кто-нибудь знает о неудачной попытке?

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

+0

Если 'boost :: tuples :: cons' является шаблоном (как вы его используете, как позже), вы должны использовать' template <...> using' вместо. Кроме того, что делает 'case_of'? Как это реализовано? – xinaiz

+1

Спасибо. Я исправил декларацию «Против». Вопрос в том, кто-то пытался реализовать что-то вроде case_of. Из кресла представляется правдоподобным, что, учитывая список аргументов и список функций, некоторые метапрограммирование можно использовать для вызова правильного или вызвать ошибку, если вызов неоднозначен. – AndyJost

+0

Из того, что я знаю, лямбда не может быть использована в SFINAE. Дело в том, что выражение должно быть действительным при вызове функции, поэтому, если соответствует только один случай, другие случаи должны быть недействительными, а затем, если они недействительны, возникает ошибка. Вы можете достичь этой функциональности с помощью шаблонов и классического SFINAE, но я сомневаюсь, что кто-то придумает определение в таких областях, как вы хотите это сделать. – xinaiz

ответ

2

На одной странице на русском Habrahabr веб-сайт о некотором виде соответствия шаблону в C++. Это позволяет вам сопоставлять типы (да, больше похоже на тип соответствия) или даже на некоторые условные условные условия компиляции. Это выглядит следующим образом:

template<class T> 
decltype(auto) test(T& value) { 
    return match(value 
     ,[](std::string value) { cout << "This is string"; return value + " Hi!"; } 
     ,[](int i)    { cout << "This is int"; return i * 100; } 
     ,[](auto a)    { cout << "This is default";return nullptr; } 
    ); 
} 

match(true_type{} 
     ,[](bool_constant< T::value == 10 >)      { cout << "1" ; } 
     ,[](bool_constant< (T::value == 20 && sizeof...(Args)>4) >) { cout << "2" ; } 
    ); 

Вы можете найти больше фрагментов кода и деталь реализации в blog post itself. Этот блог-пост вдохновлен этим C++ Mach7 библиотеки, которая является довольно безобразным блог пост мнению автора, в некоторых местах, но позволяет писать что-то вроде этого:

// Fibonacci numbers 
int fib(int n) 
{ 
    var<int> m; 

    Match(n) 
    { 
     Case(1)  return 1; 
     Case(2)  return 1; 
     Case(2*m) return sqr(fib(m+1)) - sqr(fib(m-1)); 
     Case(2*m+1) return sqr(fib(m+1)) + sqr(fib(m)); 
    } 
    EndMatch 
} 

Но не знаю о списках. Вероятно, вы можете найти другие ответы и/или реализовать свой собственный список совпадений.

+0

Спасибо. Это, безусловно, отвечает на мой вопрос. – AndyJost

0

Я могу только думать о структуре SFINAE здесь.

template<class tuple, class = void> 
struct is_empty : std::false_type 
{ }; 

template<class Tuple> 
struct is_empty< 
    Tuple, 
    std::enable_if_t< 
     std::is_base_of<Nil, Tuple>::value, void 
    > 
> : std::true_type 
{ }; 

Но помните, что работает для типа, а не для переменных. Но так как кортежи не могут изменить размер в C++, создание функции не дает нам многого. Но если вы хотите, то:

template<class Tuple> 
constexpr bool isEmpty(const Tuple&) { 
    return is_empty<Tuple>::value; 
} 

Так этот код:

boost::tuple<> t1; 
std::cout << isEmpty(t1) << std::endl; 

boost::tuple<int, float> t2; 
std::cout << isEmpty(t2) << std::endl; 

Результаты в выходе:

1 
0 

Но эти структуры должны быть определены вне функций.

+0

Я думаю, что самым близким аналогом моего примера, использующим функции вне линии, будет: 'bool is_empty (Nil) {return true; } template bool is_empty (Cons const &) {return false; } ' – AndyJost