C++17: ошибка преобразования лямбда в std::function

В настоящее время я изучаю дополнения С++ 17. После поиска с std::variant захотелось использовать и std::optional в том же примере . В настоящее время видим, что компиляция не выполняется из-за следующей ошибки:

error: no viable conversion from returned value of type
      '(lambda at ./html_parser.hpp:53:9)' to function return type 'Parser<char>' (aka
      'std::__1::function<std::__1::optional<std::__1::pair<char, std::__1::basic_string<char> > >
      (std::__1::basic_string<char>)>')
        return [=](std::string& input) -> ParserResult<char> {
               ^~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
/home/acid/tools/include/c++/v1/functional:1627:5: note: candidate constructor not viable: no known conversion
      from '(lambda at ./html_parser.hpp:53:9)' to 'std::nullptr_t' (aka 'nullptr_t') for 1st argument
    function(nullptr_t) _NOEXCEPT : __f_(0) {}
    ^
/home/acid/tools/include/c++/v1/functional:1628:5: note: candidate constructor not viable: no known conversion
      from '(lambda at ./html_parser.hpp:53:9)' to 'const
      std::__1::function<std::__1::optional<std::__1::pair<char, std::__1::basic_string<char> > >
      (std::__1::basic_string<char>)> &' for 1st argument
    function(const function&);
    ^
/home/acid/tools/include/c++/v1/functional:1629:5: note: candidate constructor not viable: no known conversion
      from '(lambda at ./html_parser.hpp:53:9)' to 'std::__1::function<std::__1::optional<std::__1::pair<char,
      std::__1::basic_string<char> > > (std::__1::basic_string<char>)> &&' for 1st argument
    function(function&&) _NOEXCEPT;
    ^
/home/acid/tools/include/c++/v1/functional:1631:5: note: candidate template ignored: requirement
      '__callable<(lambda at ./html_parser.hpp:53:9)>::value' was not satisfied [with _Fp =
      (lambda at ./html_parser.hpp:53:9)]
    function(_Fp);
    ^
1 error generated.

Чтобы разобрать HTML для получения DOM, начнем с объявления некоторых комбинаторов парсера следующим образом:

#pragma once

#include <string>
#include <utility>
#include <functional>
#include <optional>

namespace dragon {
    namespace html {
        namespace parser {
            template <typename ParserOutput, typename ParserInput = std::string>
            using ParserResult = std::optional<std::pair<ParserOutput, ParserInput>>;

            template<typename ParserOutput, typename ParserInput = std::string>
            using Parser = std::function<ParserResult<ParserOutput, ParserInput>(ParserInput)>;

            template <typename ParserOutput, typename ParserInput = std::string>
            auto parse(Parser<ParserOutput, ParserInput> p, ParserInput i) -> ParserResult<ParserOutput, ParserInput>{
                return p(i);
            }

            // few parser combinators.

            // thenP combinator: applies the first parser, if it succeeds apply the second to the rest of 
            // the input left over by the first parser.
            // currently just fails and returns empty!! does not provide any debugging info/msg 
            // as to why the parsing failed.
            template<typename FirstParser, typename SecondParser>
            auto thenP(FirstParser f, SecondParser s) {
                return [=](std::string input) -> decltype(parse(s, std::string())) {
                    auto fv = parse(f, input);

                    if (fv) {
                        auto fvv = *fv;
                        return parse(s, fvv.second);
                    }
                    else {
                        return {};
                    }
                };
            }

            template<typename FirstParser, typename SecondParser>
            auto choiceP(FirstParser f, SecondParser s) {
                return [=](std::string input) {
                    auto fv = parse(f, input);
                    if (!fv) return parse(s, input);
                    return fv;
                };
            }

            auto charP(char match) -> Parser<char> {
                return [=](std::string& input) -> ParserResult<char> {
                    if ((input.empty() == false) && (input[0] == match)) {
                        return std::make_pair(input[0], input.substr(1));
                    }
                    return {};
                };
            }
        }
    }
}

Я вижу указанную выше ошибку при попытке скомпилировать простое использование, как показано ниже:

int main()
{
    auto less = Parser::parser::charP('<');
    auto greater = Parser::parser::charP('>');

    auto lag = Parser::parser::thenP(less, greater);
    auto log = Parser::parser::choiceP(less, greater);

    auto lagv = lag("<>");
    auto logv = log("|>");

    return 0;
} 

Это отлично компилируется с Visual Studio 2017 (std=c++-latest). Но Clang выдает вышеуказанную ошибку. Попытка выяснить несоответствие между этими двумя компиляторами. и как решить эту проблему с помощью Clang.


person user3169543    schedule 21.10.2017    source источник
comment
В будущем постарайтесь сократить свой вопрос до минимально воспроизводимого примера. Здесь довольно много нерелевантного кода, из-за которого трудно понять, в чем проблема.   -  person Barry    schedule 22.10.2017


Ответы (1)


Это плохо сформировано:

auto charP(char match) -> Parser<char> {
    return [=](std::string& input) -> ParserResult<char> { ... };
}

по той же причине, что это плохо сформировано:

std::function<void(int)> f = [](int& ){};

Лямбда справа не вызывается с помощью int, только с int&, поэтому вы не можете создать из нее function<void(int)>. MSVC имеет некоторый разрешающий режим, в котором он позволяет создавать неконстантную ссылку lvalue из rvalue, возможно, поэтому это сработало.

person Barry    schedule 21.10.2017