Рекурсивная лямбда-функция С++ 11 в операторе constexpr

изучая тему функций constexpr/operator в С++ 11, я наткнулся на эту статью: http://www.codeproject.com/Articles/447922/Application-of-Cplusplus11-User-Defined-Literals-t

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

constexpr unsigned long long ToBinary(unsigned long long x, const char* s)
{
    return (!*s ? x : ToBinary(x + x + (*s =='1'? 1 : 0), s+1));
}
constexpr unsigned long long int operator "" _b(const char* s) 
{ return ToBinary(0,s);}

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

Решения для рекурсивных лямбда-выражений в C++ известны и используют использование std::function. Чтобы сделать это возможным в операторе constexpr, нужно было бы, чтобы объявление и вызов рекурсивной лямбды были встроены в один оператор return. Мои попытки добиться этого не увенчались успехом, поэтому я прибегаю к SO за помощью. Возможен ли вообще рекурсивный лямбда-вызов внутри оператора constexpr? Если да, то какие подсказки?

Спасибо,


person Wojciech Migda    schedule 03.10.2013    source источник
comment
operator() лямбды не помечен constexpr, поэтому это невозможно.   -  person Xeo    schedule 03.10.2013
comment
В C++11 лямбда-выражение не может появляться внутри константного выражения (оно может появляться внутри функции constexpr), поэтому вы не можете использовать функцию constexpr в постоянном выражении, если эта функция имеет лямбда-выражение в оцениваемом контексте. .   -  person dyp    schedule 03.10.2013
comment
@Xeo constexpr int foo(bool p) { return p ? 42 : [](){ return 255; }(); } в порядке.   -  person dyp    schedule 03.10.2013
comment
@DyP: foo(false) не будет работать как постоянное выражение.   -  person Xeo    schedule 03.10.2013


Ответы (1)


Согласно [expr.const]/2, лямбда-выражение явно запрещено быть частью основного постоянного выражения. Он может появиться в невычисленном операнде тернарного оператора, например. p ? 42 : [](){ return 255; }();, если p равно true.

Тем не менее, лямбда также может появиться в функции constexpr, однако эта часть не может быть оценена, когда функция должна использоваться в постоянном выражении. Пример:

constexpr unsigned long long int operator "" _b(const char* s) 
{
    return *s == '0' || *s == 0 ? 0 : [=]() mutable
    {
        unsigned long long ret = 0;
        for(; *s != 0; ++s)
        {
            ret <<= 1;
            if(*s == '1') ret += 1;
        }
        return ret;
    }();
}

#include <iostream>
int main()
{
    constexpr int c = 0_b;             // fine
    //constexpr int c1 = 1_b;          // error
    std::cout << 1010_b << std::endl;  // fine
}

Конечно, это не очень полезно; оператор constexpr разрешает синтаксический анализ строк как двоичных литералов в константных выражениях. Следовательно, части оператора, выполняющие это преобразование, должны быть действительными (основными) константными выражениями.

Поскольку сигнатура оператора точно указана, вы не можете повторно использовать сам оператор и передавать обработанные данные в качестве дополнительного аргумента. Вы также не можете использовать циклы в constexpr С++ 11.

Конечно, вы можете использовать пространства имен. Это может быть даже лучше для пользователя:

namespace my_literals
{
    constexpr unsigned long long ToBinary(unsigned long long x, const char* s)
    {
        return (!*s ? x : ToBinary(x + x + (*s =='1'? 1 : 0), s+1));
    }
    constexpr unsigned long long int operator "" _b(const char* s) 
    { return ToBinary(0,s);}
}

// user writes:
using my_literals::operator ""_b;
person dyp    schedule 03.10.2013
comment
Ничего себе, это объяснение, которое просто лишило меня дара речи. Краткий и полный, даже с идеями альтернативных путей. Большое спасибо! - person Wojciech Migda; 04.10.2013