无法在 MSVC 19.28 上编译变体访问者访问
Cannot compile variant visitor access on MSVC 19.28
我尝试在 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 的代码片段,它是用 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");
}
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。
作为一种潜在的解决方法,可以尝试使用模板化 operator()
将 lambda 提取到仿函数 class 中,它似乎可以编译 (example)。
我尝试在 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 的代码片段,它是用 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");
}
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。
作为一种潜在的解决方法,可以尝试使用模板化 operator()
将 lambda 提取到仿函数 class 中,它似乎可以编译 (example)。