无法在 MSVC 19.28 上编译变体访问者访问

Posted

技术标签:

【中文标题】无法在 MSVC 19.28 上编译变体访问者访问【英文标题】:Cannot compile variant visitor access on MSVC 19.28 【发布时间】:2021-05-08 19:39:09 【问题描述】:

我尝试在 Visual Studio 2019 上编译一个个人项目(使用 MSVC 19.28 编译器),但在 std::visit 中遇到了一个我不明白的编译错误:


<source>(131): error C2653: '`global namespace'': is not a class or namespace name
C:/data/msvc/14.28.29914/include\type_traits(1493): note: see reference to function template instantiation 'auto CommandLineOptionsParser<CmdLineOpts>::register_callback::<lambda_1>::()::<lambda_1>::operator ()<const _First&>(_T1) const' being compiled
        with
        [
            _First=bool CmdLineOpts::* ,
            _T1=bool CmdLineOpts::* const &
        ]
C:/data/msvc/14.28.29914/include\variant(1654): note: see reference to alias template instantiation 'std::_Variant_visit_result_t<CommandLineOptionsParser<CmdLineOpts>::register_callback::<lambda_1>::()::<lambda_1>,const std::variant<bool CmdLineOpts::* >&>' being compiled
<source>(120): note: while compiling class template member function 'void CommandLineOptionsParser<CmdLineOpts>::register_callback(const CommandLineOption &,std::variant<bool CmdLineOpts::* >)'
<source>(83): note: see reference to function template instantiation 'void CommandLineOptionsParser<CmdLineOpts>::register_callback(const CommandLineOption &,std::variant<bool CmdLineOpts::* >)' being compiled
<source>(142): note: see reference to class template instantiation 'CommandLineOptionsParser<CmdLineOpts>' being compiled
<source>(123): error C2672: 'visit': no matching overloaded function found
<source>(131): error C2893: Failed to specialize function template 'unknown-type std::visit(_Callable &&,_Variants &&...)'
C:/data/msvc/14.28.29914/include\variant(1654): note: see declaration of 'std::visit'
<source>(131): note: With the following template arguments:
<source>(131): note: '_Callable=CommandLineOptionsParser<CmdLineOpts>::register_callback::<lambda_1>::()::<lambda_1>'
<source>(131): note: '_Variants=const std::variant<bool CmdLineOpts::* > &'
<source>(131): note: '<unnamed-symbol>=void'
Compiler returned: 2

这段代码用 gcc 编译得很好。

我在std::visit 上测试了来自cppreference 的代码sn-p,它使用MSVC 编译,所以我不太确定这里有什么问题。

我简化了代码并在godbolt上重现了这个问题

这是代码

#include <algorithm>
#include <functional>
#include <iostream>
#include <map>
#include <memory>
#include <sstream>
#include <string>
#include <set>
#include <string_view>
#include <variant>
#include <type_traits>

using InvalidArgumentException = std::invalid_argument;
using CommandLineOption = std::string;

template <class Opts>
class CommandLineOptionsParser : Opts 
 public:
  using OptionType = std::variant<bool Opts::*>;
  using CommandLineOptionWithValue = std::pair<CommandLineOption, OptionType>;

  Opts parse(const char* argStr) 
    // First register the callbacks
    bool Opts::* pBool = &Opts::help;
    register_callback("help", pBool);

    for (auto& cbk : _callbacks) 
      cbk.second(0, argStr);
    

    return static_cast<Opts>(*this);
  

 private:
  using callback_t = std::function<void(int, const char *)>;

  std::map<CommandLineOption, callback_t> _callbacks;

  void register_callback(const CommandLineOption& commandLineOption, OptionType prop) 
    _callbacks[commandLineOption] = [this, &commandLineOption, prop](int idx, const char * argv) 
      if (std::string(argv) == commandLineOption) 
        std::visit([](auto&& a)  
              using T = std::decay_t<decltype(a)>;
              if constexpr (std::is_same_v<T, bool Opts::*>) 
                std::cout << "bool" << std::endl;
              
            ,
            prop);
      
    ;
  ;
;

struct CmdLineOpts 
  bool help;
;

int main(int argc, const char* argv[])

    CommandLineOptionsParser<CmdLineOpts> p;
    CmdLineOpts cmdLineOptions = p.parse("opt1");    

【问题讨论】:

将此code.cpp 文件归档为带有Visual Studio 的错误。告诉他们它不能在包括 16.10 Preview 2.1 在内的 VS 2019 上编译,但确实使用 clang-cl 和 GCC 编译。 MSVC 团队计划很快宣布完全支持 C++20,因此他们应该能够解决这个问题。 @ChuckWalbourn 我简化了代码以消除对 c++20 和 std::span 的依赖,问题仍然存在。 @StéphaneJanel 将其报告为 MSVC C++ 编译器的错误。 【参考方案1】:

似乎 MSVC 难以在模板上下文中使用指向成员参数的指针来合成 lambda。

我试图将其简化为 MCVE,希望它能抓住问题的本质:

template<class T>
bool test(int T::* t) 
    return [](int T::* x) 
        return true;
    (t);


struct A 
    int a;
;

int main() 
    return test<A>(&A::a);

它无法在 MSVC C++20 模式(但不是 C++17)下编译,并出现类似的无意义错误 (link):

<source>(5): error C2653: '`global namespace'': is not a class or namespace name
<source>(13): note: see reference to function template instantiation 'bool test<A>(int A::* )' being compiled
<source>(5): error C2664: 'bool test::<lambda_1>::operator ()(A *) const': cannot convert argument 1 from 'int A::* ' to 'A *'
<source>(5): note: There is no context in which this conversion is possible
<source>(5): note: see declaration of 'test::<lambda_1>::operator ()'

我建议将此报告给vendor。

作为一种潜在的解决方法,可以尝试将 lambda 提取到具有模板化 operator() 的仿函数类中,它似乎可以编译 (example)。

【讨论】:

非常感谢 @rustyx,感谢您的 MCVE,我打开了一张票 here,他们已经在修复问题了。

以上是关于无法在 MSVC 19.28 上编译变体访问者访问的主要内容,如果未能解决你的问题,请参考以下文章

Hdf5开发笔记:hdf5介绍,在windows上编译msvc2015x64版本

vs2015上编译QT程序的环境搭建

帮助在 Windows 上编译 seek-bzip2

在没有 X 的 Linux 上编译 Qt 应用程序

工作 MSVC 代码的 Clang 中的“使用不同类型的 Typedef 重新定义”错误

在 64 位 Arm 上编译和安装 Chez 方案?