C++ 与msvc相比,键入擦除、委托和lambda函数

C++ 与msvc相比,键入擦除、委托和lambda函数,c++,c++11,templates,lambda,visual-studio-2015,C++,C++11,Templates,Lambda,Visual Studio 2015,在下面的代码中,我使用了一种相对简单的类型擦除技术。类解释器上下文ptr定义了一个“接口”,指向实现该接口的对象的指针可用于构造解释器上下文ptr。这允许多态性而不使用虚拟分派 这与一篇名为。另外,请在github上查看badair的项目 另外,如果您一开始不认识它,这里的+[](…)语法就是所谓的“正lambda”语法,这是一个很好的解释 以下是MCVE: #include <iostream> #include <string> #include <vector

在下面的代码中,我使用了一种相对简单的类型擦除技术。类
解释器上下文ptr
定义了一个“接口”,指向实现该接口的对象的指针可用于构造
解释器上下文ptr
。这允许多态性而不使用虚拟分派

这与一篇名为。另外,请在github上查看badair的项目

另外,如果您一开始不认识它,这里的
+[](…)
语法就是所谓的“正lambda”语法,这是一个很好的解释

以下是MCVE:

#include <iostream>
#include <string>
#include <vector>

class interpreter_context_ptr {
  void * object_;
  void (*new_text_call_)(void *, const std::string &);
  void (*error_text_call_)(void *, const std::string &);
  void (*clear_input_call_)(void *);

public:
  void new_text(const std::string & str) const {
    this->new_text_call_(object_, str);
  }

  void error_text(const std::string & str) const {
    this->error_text_call_(object_, str);
  }

  void clear_input() const { this->clear_input_call_(object_); }

  template <typename T>
  explicit interpreter_context_ptr(T * t)
    : object_(static_cast<void *>(t))
    , new_text_call_(+[](void * o, const std::string & str) {
      static_cast<T *>(o)->new_text(str);
    })
    , error_text_call_(+[](void * o, const std::string & str) {
      static_cast<T *>(o)->error_text(str);
    })
    , clear_input_call_(+[](void * o) { static_cast<T *>(o)->clear_input(); }) {
  }
};

/***
 * Tests
 */

struct A {
  void new_text(const std::string & str) {
    std::cout << "A: " << str << std::endl;
  }

  void error_text(const std::string & str) {
    std::cout << "A! " << str << std::endl;
  }
  void clear_input() { std::cout << std::endl; }
};

struct B {
  void new_text(const std::string & str) {
    std::cout << "B: " << str << std::endl;
  }

  void error_text(const std::string & str) {
    std::cout << "B! " << str << std::endl;
  }
  void clear_input() { std::cout << std::endl; }
};

int main() {
  std::vector<interpreter_context_ptr> stack;

  A a;
  B b;

  stack.emplace_back(&a);
  stack.back().new_text("1");
  stack.emplace_back(&b);
  stack.back().new_text("2");
  stack.emplace_back(&b);
  stack.back().new_text("3");
  stack.back().clear_input();
  stack.pop_back();
  stack.back().error_text("4");
  stack.emplace_back(&a);
  stack.back().error_text("5");
  stack.pop_back();
  stack.back().error_text("6");
  stack.pop_back();
  stack.back().new_text("7");

  stack.back().clear_input();
  stack.pop_back();
  std::cout << "Stack size = " << stack.size() << std::endl;
}
结果MSVC仍然不高兴——主要的错误是神秘的

编译器找到了一个不需要的标识符。确保在使用标识符之前声明了它

初始值设定项可以用括号括起来。要避免此问题,请将声明符括在括号中或将其设为typedef

当编译器将表达式检测为类模板参数时,也可能会导致此错误;使用typename告诉编译器它是一个类型

以下是完整的日志:

Error(s):

source_file.cpp(27): error C2061: syntax error: identifier 'T'
C:\Program Files (x86)\Microsoft Visual Studio 14.0\VC\INCLUDE\xmemory0(655): note: see reference to function template instantiation 'interpreter_context_ptr::interpreter_context_ptr<A>(T *)' being compiled
        with
        [
            T=A
        ]
C:\Program Files (x86)\Microsoft Visual Studio 14.0\VC\INCLUDE\xmemory0(773): note: see reference to function template instantiation 'void std::allocator<_Ty>::construct<_Objty,A>(_Objty *,A &&)' being compiled
        with
        [
            _Ty=interpreter_context_ptr,
            _Objty=interpreter_context_ptr
        ]
C:\Program Files (x86)\Microsoft Visual Studio 14.0\VC\INCLUDE\xmemory0(773): note: see reference to function template instantiation 'void std::allocator<_Ty>::construct<_Objty,A>(_Objty *,A &&)' being compiled
        with
        [
            _Ty=interpreter_context_ptr,
            _Objty=interpreter_context_ptr
        ]
C:\Program Files (x86)\Microsoft Visual Studio 14.0\VC\INCLUDE\xmemory0(918): note: see reference to function template instantiation 'void std::allocator_traits<_Alloc>::construct<_Ty,A>(std::allocator<_Ty> &,_Objty *,A &&)' being compiled
        with
        [
            _Alloc=std::allocator<interpreter_context_ptr>,
            _Ty=interpreter_context_ptr,
            _Objty=interpreter_context_ptr
        ]
C:\Program Files (x86)\Microsoft Visual Studio 14.0\VC\INCLUDE\xmemory0(917): note: see reference to function template instantiation 'void std::allocator_traits<_Alloc>::construct<_Ty,A>(std::allocator<_Ty> &,_Objty *,A &&)' being compiled
        with
        [
            _Alloc=std::allocator<interpreter_context_ptr>,
            _Ty=interpreter_context_ptr,
            _Objty=interpreter_context_ptr
        ]
C:\Program Files (x86)\Microsoft Visual Studio 14.0\VC\INCLUDE\vector(929): note: see reference to function template instantiation 'void std::_Wrap_alloc<std::allocator<_Ty>>::construct<interpreter_context_ptr,A>(_Ty *,A &&)' being compiled
        with
        [
            _Ty=interpreter_context_ptr
        ]
C:\Program Files (x86)\Microsoft Visual Studio 14.0\VC\INCLUDE\vector(928): note: see reference to function template instantiation 'void std::_Wrap_alloc<std::allocator<_Ty>>::construct<interpreter_context_ptr,A>(_Ty *,A &&)' being compiled
        with
        [
            _Ty=interpreter_context_ptr
        ]
source_file.cpp(68): note: see reference to function template instantiation 'void std::vector<interpreter_context_ptr,std::allocator<_Ty>>::emplace_back<A*>(A *&&)' being compiled
        with
        [
            _Ty=interpreter_context_ptr
        ]
source_file.cpp(68): note: see reference to function template instantiation 'void std::vector<interpreter_context_ptr,std::allocator<_Ty>>::emplace_back<A*>(A *&&)' being compiled
        with
        [
            _Ty=interpreter_context_ptr
        ]
source_file.cpp(30): error C2061: syntax error: identifier 'T'
source_file.cpp(32): error C2061: syntax error: identifier 'T'

但正如我在评论中所写,如果我真的要走这么远,我会感到非常惊讶。MSVC开发人员声称已经在上完全支持并实现了lambda函数。我认为应该包括在lambda的范围内引用环境模板参数。

在这个问题的时候,Visual C++ 2015编译器在lambda上遇到了<代码>运算符+ < /代码>的问题。删除每个lambda前面的
+
符号可以编译此代码。它确实稍微改变了语义(表达式的有效类型略有不同),尽管我认为这并不重要


确保安装了最新的更新,以确保没有其他编译器/库错误让您无故浪费时间。

MSVC在
+
中存在问题,因为它将lambda转换为多个函数指针调用约定类型;所以
+
是不明确的

通常,删除
+
会使代码“正常工作”,因为隐式强制转换操作符做了正确的事情,并且知道要将lambda强制转换到的指针的调用约定

这不是你唯一的问题。下一个问题是无状态lambda不能正确地访问周围上下文中可用的类型

MSVC 2015名义上只是一个C++11编译器。它有很多很多不正常的情况。随着编译器的更新,对这类事情的支持往往会大大提高;确保您拥有MSVC 2015 U3.1(有一个U3,然后是U3上的一个补丁,我称之为U3.1)


另一个次要细节是处理
T
const
类型的情况。我变懒了,用
(void*)t
替换
静态_cast(t)
,因为它将在一个步骤中删除const和cast to void。或者你可以
const_cast(static_cast(t))
或者类似的东西。

你使用的是VS2015,它与Clang/C2编译器前端一起提供。如果您无法理解MSVC向您抛出的错误消息,请尝试其他编译器。请注意,Clang可能接受此代码,而MSVC不接受。那你就不走运了。但是如果不是,你肯定会得到一个更清晰的错误消息。嗯,不幸的是,看起来至少是真正的Clang和GCC接受这个代码没有警告,所以我的选择可能是不去的,虽然Clang /C2确实使用微软C++库,这可能是错误的原因。如果是这样的话,那就是一个库错误。另外,试着从lambda中删除
+
,我已经读过了,在MSVC阻塞该构造之前。代码在没有它们的情况下编译,但请检查行为是否相同。@rubenvb:我的意思是,它怎么可能是库呢?
interpreter\u context\u ptr
类在这里不使用标准库,保存
std::string
,我认为这不相关,我认为我们可以用
int
替换它(我想我可以测试一下)除非lambda函数的实现以某种方式依赖于msvc中的库,否则我看不出这会在哪里出现,这不应该只是核心语言功能吗?嗯,当您从lambda中删除
+
es时,它会编译。所以我不确定其他所有的错误都来自哪里。
  template <typename T>
  explicit interpreter_context_ptr(T * t)
    : object_(static_cast<void *>(t))
    , new_text_call_([](void * o, const std::string & str) {
      static_cast<T *>(o)->new_text(str);
    })
    , error_text_call_([](void * o, const std::string & str) {
      static_cast<T *>(o)->error_text(str);
    })
    , clear_input_call_([](void * o) { static_cast<T *>(o)->clear_input(); }) {
  }
Error(s):

source_file.cpp(27): error C2061: syntax error: identifier 'T'
C:\Program Files (x86)\Microsoft Visual Studio 14.0\VC\INCLUDE\xmemory0(655): note: see reference to function template instantiation 'interpreter_context_ptr::interpreter_context_ptr<A>(T *)' being compiled
        with
        [
            T=A
        ]
C:\Program Files (x86)\Microsoft Visual Studio 14.0\VC\INCLUDE\xmemory0(773): note: see reference to function template instantiation 'void std::allocator<_Ty>::construct<_Objty,A>(_Objty *,A &&)' being compiled
        with
        [
            _Ty=interpreter_context_ptr,
            _Objty=interpreter_context_ptr
        ]
C:\Program Files (x86)\Microsoft Visual Studio 14.0\VC\INCLUDE\xmemory0(773): note: see reference to function template instantiation 'void std::allocator<_Ty>::construct<_Objty,A>(_Objty *,A &&)' being compiled
        with
        [
            _Ty=interpreter_context_ptr,
            _Objty=interpreter_context_ptr
        ]
C:\Program Files (x86)\Microsoft Visual Studio 14.0\VC\INCLUDE\xmemory0(918): note: see reference to function template instantiation 'void std::allocator_traits<_Alloc>::construct<_Ty,A>(std::allocator<_Ty> &,_Objty *,A &&)' being compiled
        with
        [
            _Alloc=std::allocator<interpreter_context_ptr>,
            _Ty=interpreter_context_ptr,
            _Objty=interpreter_context_ptr
        ]
C:\Program Files (x86)\Microsoft Visual Studio 14.0\VC\INCLUDE\xmemory0(917): note: see reference to function template instantiation 'void std::allocator_traits<_Alloc>::construct<_Ty,A>(std::allocator<_Ty> &,_Objty *,A &&)' being compiled
        with
        [
            _Alloc=std::allocator<interpreter_context_ptr>,
            _Ty=interpreter_context_ptr,
            _Objty=interpreter_context_ptr
        ]
C:\Program Files (x86)\Microsoft Visual Studio 14.0\VC\INCLUDE\vector(929): note: see reference to function template instantiation 'void std::_Wrap_alloc<std::allocator<_Ty>>::construct<interpreter_context_ptr,A>(_Ty *,A &&)' being compiled
        with
        [
            _Ty=interpreter_context_ptr
        ]
C:\Program Files (x86)\Microsoft Visual Studio 14.0\VC\INCLUDE\vector(928): note: see reference to function template instantiation 'void std::_Wrap_alloc<std::allocator<_Ty>>::construct<interpreter_context_ptr,A>(_Ty *,A &&)' being compiled
        with
        [
            _Ty=interpreter_context_ptr
        ]
source_file.cpp(68): note: see reference to function template instantiation 'void std::vector<interpreter_context_ptr,std::allocator<_Ty>>::emplace_back<A*>(A *&&)' being compiled
        with
        [
            _Ty=interpreter_context_ptr
        ]
source_file.cpp(68): note: see reference to function template instantiation 'void std::vector<interpreter_context_ptr,std::allocator<_Ty>>::emplace_back<A*>(A *&&)' being compiled
        with
        [
            _Ty=interpreter_context_ptr
        ]
source_file.cpp(30): error C2061: syntax error: identifier 'T'
source_file.cpp(32): error C2061: syntax error: identifier 'T'
class interpreter_context_ptr {
  void * object_;
  void (*new_text_call_)(void *, const std::string &);
  void (*error_text_call_)(void *, const std::string &);
  void (*clear_input_call_)(void *);

  template <typename T>
  struct helper {
    static void new_text(void * o, const std::string & str) {
      static_cast<T*>(o)->new_text(str);
    }
    static void error_text(void * o, const std::string & str) {
      static_cast<T*>(o)->error_text(str);
    }
    static void clear_input(void * o) {
      static_cast<T*>(o)->clear_input();
    }
  };

public:
  void new_text(const std::string & str) const {
    this->new_text_call_(object_, str);
  }

  void error_text(const std::string & str) const {
    this->error_text_call_(object_, str);
  }

  void clear_input() const { this->clear_input_call_(object_); }

  template <typename T>
  explicit interpreter_context_ptr(T * t)
    : object_(static_cast<void *>(t))
    , new_text_call_(&helper<T>::new_text)
    , error_text_call_(&helper<T>::error_text)
    , clear_input_call_(&helper<T>::clear_input) {
  }
};