c++ - 类型删除、委托(delegate)和 lambda 函数与 msvc

标签 c++ c++11 templates lambda visual-studio-2015

在下面的代码中,我使用了一种相对简单的类型删除技术。 interpreter_context_ptr 类定义了一个“接口(interface)”,指向实现该接口(interface)的对象的指针可用于构造一个interpreter_context_ptr。这允许在不使用虚拟分派(dispatch)的情况下实现多态。

这与一篇名为Impossibly Fast Delegates的旧文章极其相似.另外,查看 eraserface badair 在 github 上的项目。

还有,如果你一开始不认识,这里的+[](...)语法就是所谓的“positive lambda”语法,here很好的解释。

这是 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;
}

被测代码非常简单,只有几行,在我最近几个月做的一个项目中,在gcc和clang中运行良好。

但是,我从 MSVC 收到了一些我不理解的非常简洁的错误消息。

首先,它提示正 lambda,我认为它不应该这样。 (我从 rextester.com 粘贴这些错误)

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(28): error C2593: 'operator +' is ambiguous
source_file.cpp(28): note: could be 'built-in C++ operator+(void (__cdecl *)(void *,const std::string &))'
source_file.cpp(28): note: or       'built-in C++ operator+(void (__stdcall *)(void *,const std::string &))'
source_file.cpp(28): note: or       'built-in C++ operator+(void (__fastcall *)(void *,const std::string &))'
source_file.cpp(28): note: or       'built-in C++ operator+(void (__vectorcall *)(void *,const std::string &))'
source_file.cpp(28): note: while trying to match the argument list '(interpreter_context_ptr::<lambda_3268dba1ab087602b708c8fa2c92932b>)'
source_file.cpp(30): error C2061: syntax error: identifier 'T'
source_file.cpp(31): error C2593: 'operator +' is ambiguous
source_file.cpp(31): note: could be 'built-in C++ operator+(void (__cdecl *)(void *,const std::string &))'
source_file.cpp(31): note: or       'built-in C++ operator+(void (__stdcall *)(void *,const std::string &))'
source_file.cpp(31): note: or       'built-in C++ operator+(void (__fastcall *)(void *,const std::string &))'
source_file.cpp(31): note: or       'built-in C++ operator+(void (__vectorcall *)(void *,const std::string &))'
source_file.cpp(31): note: while trying to match the argument list '(interpreter_context_ptr::<lambda_b251fc93653023678ada88e17e2a71b3>)'
source_file.cpp(32): error C2061: syntax error: identifier 'T'
source_file.cpp(32): error C2593: 'operator +' is ambiguous
source_file.cpp(32): note: could be 'built-in C++ operator+(void (__cdecl *)(void *))'
source_file.cpp(32): note: or       'built-in C++ operator+(void (__stdcall *)(void *))'
source_file.cpp(32): note: or       'built-in C++ operator+(void (__fastcall *)(void *))'
source_file.cpp(32): note: or       'built-in C++ operator+(void (__vectorcall *)(void *))'
source_file.cpp(32): note: while trying to match the argument list '(interpreter_context_ptr::<lambda_3ba87b1970191b4772ddfa67a05f70ea>)'
source_file.cpp(28): error C2088: '+': illegal for class
source_file.cpp(31): error C2088: '+': illegal for class
source_file.cpp(32): error C2088: '+': illegal for class

好吧,微软的“正 lambda”理论有问题,其中 lambda 被隐式转换为函数指针,而一元 operator + 是空操作。没关系,让我们去掉正 lambda。

  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(); }) {
  }

原来 MSVC 仍然不满意——主要错误是神秘的 C2061 .

The compiler found an identifier where it wasn't expected. Make sure that identifier is declared before you use it.

An initializer may be enclosed by parentheses. To avoid this problem, enclose the declarator in parentheses or make it a typedef.

This error could also be caused when the compiler detects an expression as a class template argument; use typename to tell the compiler it is a type.

这是完整的日志:

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'

我尝试了他们的建议,将 typename 放在 T 之前的所有地方,但没有任何帮助,而且我没有收到任何更多信息性错误消息。


这里有什么故事? C2061“标识符”错误在这里是什么意思,这意味着 MSVC 无法跟踪 lambda 主体内部的模板参数,或者它解析错误并认为 T 是一个变量并且不是类型之类的?

像 MSVC 2015 这样的模板函数中的 lambda 是不可能引用模板参数的吗?

我是否只需要分解出 lambda 并改为创建更多静态模板函数?


事实证明,如果我完全摆脱 lambda 表达式并像这样使用模板函数,那么 MSVC 会很好地编译它:

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) {
  }
};

但正如我在评论中所写,如果我真的必须走这么远,我会感到非常惊讶。 MSVC 开发人员声称已在 this C++11 feature page 上完全支持和实现 lambda 函数.我认为这应该包括能够在 lambda 范围内引用环境模板参数。

最佳答案

在提出这个问题时,Visual C++ 2015 编译器在 lambda 上遇到 operator+ 问题。删除每个 lambda 前面的 + 符号可以编译。它确实稍微改变了语义(表达式的有效类型略有不同),但我认为这在这里并不重要。

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

关于c++ - 类型删除、委托(delegate)和 lambda 函数与 msvc,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/40628731/

相关文章:

c++ - 函数指针和 C++ 模板

C++如何将 map 复制到 vector

C++ 字母构建顺序

c++ - 非标准代码,还是 g++ 错误?

c++ - 如何使用 vector 和对象构造函数初始化对象 vector ?

javascript - 使用 AngularJs 模板刷新页面

git - 在 git init 上设置默认目录结构

c++ - 缩放 Qt3D 的相机

c++ - 函数返回的 decltype

java - 速度:使用分隔符/前缀连接可选字段