Ошибка компилятора в шаблоне функции с VS2010 SP1

Почему я получаю отмеченную ошибку компилятора (C2899)? Я пробовал с VS2010 SP1.

#include <list>
#include <vector>
#include <algorithm>

template <typename source_container_type, typename target_container_type>
void copy_all(const source_container_type& source, target_container_type& target)
 {
    std::for_each(begin(source), end(source), [&] (const typename source_container_type::value_type& element)
    {
        // error C2899: typename cannot be used outside a template declaration
        // error C2653: 'target_container_type' : is not a class or namespace name
         target.push_back(typename target_container_type::value_type(element));
    });
}

int main()
{
    std::vector<int> a;
    a.push_back(23);
    a.push_back(24);
    a.push_back(25);

    std::list<int> b;
    copy_all(a, b);
}

С уважением
Саймон

PS: я знаю, что мог бы использовать std::copy(..) с std::back_inserter(..), но это не главное.

ИЗМЕНИТЬ

На вопрос ответил perreal в комментарии: http://connect.microsoft.com/VisualStudio/feedback/details/694857/bug-in-lambda-expressions

ИЗМЕНИТЬ

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


person Simon    schedule 27.02.2012    source источник
comment
Это похоже на ошибку: connect.microsoft.com/ VisualStudio/feedback/details/694857/. Ваш код работает в g++ 4.6.2   -  person perreal    schedule 27.02.2012
comment
Лямбда играет здесь некоторую роль, потому что без нее она работает.   -  person J.N.    schedule 27.02.2012
comment
Определение проблемного типа помогает. typedef typename target_container_type::value_type target_vt; [...] target.push_back(target_vt(element)); работает.   -  person J.N.    schedule 27.02.2012
comment
У меня нет под рукой экземпляра Visual Studio, но не могли бы вы обойти это, указав typedef локально в функции для value_type?   -  person Nim    schedule 27.02.2012
comment
@ JN, может быть, тебе следует опубликовать это как ответ?   -  person Nim    schedule 27.02.2012
comment
@ J.N & Nim: Спасибо за обходной путь, но это не то, что меня интересует. Я просто хотел знать, должно ли оно компилироваться или нет.   -  person Simon    schedule 27.02.2012


Ответы (4)


Ваша строка действительна: http://ideone.com/qAF7r

Даже древний g++ 4.3 его компилирует. Так что это, вероятно, ошибка в вашем компиляторе MS.

person bitmask    schedule 27.02.2012

Извините, VS2010 недоступен. Попробуйте переместить typedef за пределы лямбда. Работает на g++.

#include <list> 
#include <vector> 
#include <algorithm> 

template <typename source_container_type, typename target_container_type> 
void copy_all(const source_container_type& source, target_container_type& target) 
 {
    typedef typename target_container_type::value_type TargetType; /// Code change here.

    std::for_each(source.begin(), source.end(), [&] (const typename source_container_type::value_type& element) 
    { 
         target.push_back(TargetType(element)); 
    }); 
} 

int main() 
{ 
    std::vector<int> a; 
    a.push_back(23); 
    a.push_back(24); 
    a.push_back(25); 

    std::list<int> b; 
    copy_all(a, b); 
} 
person Jagannath    schedule 27.02.2012
comment
Обратите внимание, меня не интересует обходной путь — мне интересно, почему сомнительный код выдает ошибку. - person Simon; 27.02.2012

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

target.push_back (элемент);

person Alastair Taylor    schedule 27.02.2012
comment
Это необходимо, если target_container_type::value_type имеет явный конструктор. - person Simon; 27.02.2012

Поскольку вы используете С++ 0x/11, вы можете использовать:

target.push_back( (decltype(element))(element)); 
person Ajay    schedule 27.02.2012
comment
Я только что попробовал в идеоне и не скомпилировал. На самом деле разные компиляторы имеют разные проблемы соответствия. Например. g++ не компилируется, если мы используем глобальные std::begin и std::end. - person Jagannath; 27.02.2012
comment
как вы думаете, что оценивает decltype, тип исходного элемента или тип элемента назначения? - person Nim; 27.02.2012
comment
Он оценивается по типу (скажем, T). Тогда следующее выражение означает T(e), которое вызывает конструктор. - person Ajay; 27.02.2012
comment
Тип элемента назначения @Nim - person Jagannath; 27.02.2012
comment
@Jagannath: ideone использует старую версию g++, которая не полностью поддерживает C++11. - person bitmask; 27.02.2012
comment
Ну да. Я допустил ошибку, что element относится к типу источника, а не к типу назначения. И поэтому он не будет вызывать конструктор целевого типа. Я буду исследовать больше на этом! - person Ajay; 27.02.2012
comment
Одно я знаю точно. VC10 не наследует пространство имен. Таким образом, даже если функция, вызывающая labmda, включает std, вы должны использовать спецификацию std:: в лямбде! - person Ajay; 27.02.2012
comment
Да. VC11 компилирует код (как оригинальный, так и предоставленный на сайте MS). - person Ajay; 27.02.2012
comment
Это обсуждение не отвечает на вопрос - смотрите мои правки в вопросе. - person Simon; 27.02.2012