使用 Visual Studio 2017 年的编译器在 C++17 中使用 lambda 编译 Kevlin Henney 的 Fizzbuzz

Compiling Kevlin Henney's Fizzbuzz with the use of lambdas in C++17 using Visual Studio 2017's compiler

我在编程时关注 Kevlin Henney's Youtube video Lambdas。在他的视频中大约 20:30 - 20:40 处,他给出了以下代码片段:

string fizzbuzz(int n)
{
    auto fizz = [=](function<string(string)> f)
    {
        return n % 3 == 0 ? [=](auto) {return "Fizz" + f("");} : f;
    };
    auto buzz = [=](function<string(string)> f)
    {
        return n % 5 == 0 ? [=](auto) {return "Buzz" + f("");} : f;
    };
    auto id = [](auto s) { return s; };
    return fizz(buzz(id))(to_string(n));
}

这是我 IDE 中的实际代码:

#include <functional>
#include <iostream>
#include <string>

std::string fizzbuzz(int n) {
    auto fizz = [=](std::function<std::string(std::string)> f) {
        return n % 3 == 0 ? [=](auto) { return "Fizz" + f(""); } : f;
    };
    auto buzz = [=](std::function<std::string(std::string)> f) {
        return n % 5 == 0 ? [=](auto) { return "Buzz" + f(""); } : f;
    };
    auto id = [](auto s) { return s; };
    return fizz(buzz(id))(std::to_string(n));
}

int main() {
    for (int i = 1; i <= 100; i++)
        std::cout << fizzbuzz(i) << '\n';
    return 0;
}

但是,当我尝试编译此 Visual Studio 时生成了这些编译器错误:

1>------ Build started: Project: Data Structure Samples, Configuration: Debug x64 ------
1>main.cpp
1>c:\...\main.cpp(62): error C2445: result type of conditional expression is ambiguous: types 'fizzbuzz::<lambda_9027e592dd51e6f4c5342b61ff8c23f0>::()::<lambda_2463463a8046fa170a40e78d59e9f461>' and 'std::function<std::string (std::string)>' can be converted to multiple common types
1>c:\...\main.cpp(62): note: could be 'fizzbuzz::<lambda_9027e592dd51e6f4c5342b61ff8c23f0>::()::<lambda_2463463a8046fa170a40e78d59e9f461>'
1>c:\...\main.cpp(62): note: or       'std::function<std::string (std::string)>'
1>c:\...\main.cpp(65): error C2445: result type of conditional expression is ambiguous: types 'fizzbuzz::<lambda_c18a2fee5ba13240be9b86f815911a7c>::()::<lambda_2774da13f447e3dfb583778d4ea6d5bd>' and 'std::function<std::string (std::string)>' can be converted to multiple common types
1>c:\...\main.cpp(65): note: could be 'fizzbuzz::<lambda_c18a2fee5ba13240be9b86f815911a7c>::()::<lambda_2774da13f447e3dfb583778d4ea6d5bd>'
1>c:\...\main.cpp(65): note: or       'std::function<std::string (std::string)>'
1>c:\...\main.cpp(68): error C2664: 'void fizzbuzz::<lambda_9027e592dd51e6f4c5342b61ff8c23f0>::operator ()(std::function<std::string (std::string)>) const': cannot convert argument 1 from 'void' to 'std::function<std::string (std::string)>'
1>c:\...\main.cpp(68): note: Expressions of type void cannot be converted to other types
1>Done building project "Data Structure Samples.vcxproj" -- FAILED.
========== Build: 0 succeeded, 1 failed, 0 up-to-date, 0 skipped ==========

当我对照 CompilerExplorer 进行检查时发现 here,此代码可与所有三个主要编译器一起编译:GCC、Clang 和 MSVC...

我知道 C++17 同时支持 lambdasstd::function<T>,但这种类型的实现或用法是否特定于较新版本的编译器?意思是这种技术或用法仅适用于 C++20 及更高版本吗?如果是这样,可以对这个代码片段做些什么,以便它可以在 Visual Studio 2017 下使用 C++17 编译,提供相同的语义和行为?


编辑

以下是关于 C/C++ 语言部分的所有编译器命令行设置:

/JMC /permissive- /GS /W3 /Zc:wchar_t /Qspectre /ZI /Gm- /Od /sdl /Fd"x64\Debug\vc141.pdb" /Zc:inline /fp:precise /D "_DEBUG" /D "_CONSOLE" /D "_UNICODE" /D "UNICODE" /errorReport:prompt /WX- /Zc:forScope /RTC1 /Gd /MDd /std:c++latest /FC /Fa"x64\Debug\" /EHsc /nologo /Fo"x64\Debug\" /Fp"x64\Debug\Data Structure Samples.pch" /diagnostics:classic

当存在 /permissive- 标志时,MSVC 似乎在处理三元语句的推导方面存在问题。 /permissive- 标志 应该 强制 MSVC 使用 strict standards conformance, however ironically this appears to be breaking what 编译。

这可以很容易地在编译器资源管理器上重现:Live Example. This appears to be fixed in newer MSVC versions with v19.25 onward Live Example


不幸的是,由于这是一个编译器错误,因此几乎没有办法解决这个问题。 您仅限于:

  • 正在更新您的编译器,
  • 删除 /permissive-,或
  • 不使用 return 不同类型的三元语句(例如,使用具有不同 return 的 if/else

这是一个错误;如果 b 可以转换为 c 类型的右值而不是相反(在其他规则中),则 a?b:c 是合法的。

修复:

auto fizz = [=](std::function<std::string(std::string)> f) {
    if(n%3 == 0)
      f = [=](auto) { return "Fizz" + f(""); };
    return f;
};
auto buzz = [=](std::function<std::string(std::string)> f) {
    if(n % 5 == 0)
      f = [=](auto) { return "Buzz" + f(""); };
    return f;
};

应该可以。