Не удается создать экземпляр шаблона функции, который использует decltype для вывода возвращаемого типа, если он вызывается из лямбды? - PullRequest
9 голосов
/ 25 февраля 2010

Я пытаюсь использовать C ++ 0x, и в частности лямбда-выражения и decltype, чтобы упростить часть моего кода, используя компилятор MSVC10 RC.

Я столкнулся со следующей очень странной проблемой:

template <typename F>
auto foo(F f) -> decltype(f()){
  return f();
}

template <typename F>
void bar(F f){
  f();
}

int main() {
  bar([](){
    foo([]() { }); // error C2893: Failed to specialize function template ''unknown-type' foo(F)'
  });
}

Как указано в комментарии, компилятор выдает ошибку в строке foo([]() { }).

Мне не хочется кричать "ошибка компилятора", но я действительно не вижу хорошего объяснения этой ошибке. По-видимому, находясь внутри внешнего лямбда-выражения, компилятор не может специализировать шаблон функции foo для внутренней лямбды.

Однако, если определение foo изменяется на жесткий тип возвращаемого значения, например:

template <typename F>
void foo(F f){
  return f();
}

тогда все компилируется просто отлично.

Есть ли какая-то неясная причуда decltype, когда он используется для определения типа возвращаемого значения параметров лямбда-выражения в области видимости другой лямбды, о которой я не знаю?

Ответы [ 2 ]

3 голосов
/ 25 февраля 2010

Это всего лишь несколько тестов, которые люди могут наблюдать.

Работает

template <typename F>
auto foo(F f) -> decltype(f())
{
  return f();
}

void dummy() {}

int main()
{
    auto x = []()
            {   // non-lambda parameter
                foo(dummy);
            };
}

template <typename F>
auto foo(F f) -> decltype(f())
{
  return f();
}

int main()
{
    auto f = [](){};
    auto x = [&]()
            {    // pre-defined lambda
                foo(f);
            };
}

Сбой

template <typename F>
auto foo(F f) -> decltype(f())
{
  return f();
}

int main()
{
    auto x = []()
            {   // in-argument lambda
                foo([]{});
            };
}

template <typename F>
auto foo(F f) -> decltype(f())
{
  return f();
}

int main()
{
    auto x = []()
            {   // in-scope lambda
                auto f = []{};
                foo(f);
            };
}

template <typename F>
auto foo(F f) -> decltype(f())
{
  return f();
}

int main()
{
    auto x = []()
            {   // in-scope lambda, explicit return
                // (explicit return type fails too, `-> void`)
                auto f = [](){ return; };
                foo(f);
            };
}

template <typename F>
auto foo(F f) -> decltype(f())
{
  return f();
}

int main()
{
    auto x = []()
            {   // in-argument lambda, explicit return non-void
                // (explicit return type fails too, `-> int`)
                foo([]{ return 5; }); 
            };
}

Так что, похоже, это имеет отношение к области действия и типу void внутренней лямбды, даже если это сделано явным образом. (?)

0 голосов
/ 25 февраля 2010

Характер 'auto' позволяет компилятору вычислять тип. Но ваш первый пример содержит рекурсивные ссылки друг на друга, поэтому для вычисления auto для foo вам нужна строка, а для создания экземпляра bar - foo.

С другой стороны, второй пример явно говорит компилятору: «Это должен быть указатель на функцию, так что успокойтесь на время». Поскольку указатель на функцию хорошо рассчитан, компилятор типов знает, что именно будет зарезервировано. Просто для аналога: сравните предварительную декларацию участника

struct A; //forward
...
A a1; //this is an error
A *a2; //this is correct since pointer calculated in bytes
...