【问题标题】:how to decide on 'auto' return type at compile time?如何在编译时决定“自动”返回类型?
【发布时间】:2017-11-23 10:06:26
【问题描述】:

在我的天真中,我认为这会编译。唉,不是。我正在使用完全更新的 Visual Studio 2017 社区。​​p>

 namespace basic_problem {  
   auto msvc_does_not_compile = [] ( auto _string ) 
    -> std::vector< decltype(_string) >
  {
    return std::vector<decltype(_string)>{};
  };
}

编译尝试:

int main(int argc, char* argv[])
{
  auto vec = basic_problem::msvc_does_not_compile(std::string{});
  return 0 ;
}

生产:

error C2514: 'std::vector<unknown-type,std::allocator<_Ty>>': class has no constructors
1>        with
1>        [
1>            _Ty=unknown-type
1>        ]
1>        program.cpp(97): note: see declaration of 'std::vector<unknown-
           type,std::allocator<_Ty>>'
1>        with
1>        [
1>            _Ty=unknown-type
1>        ]

我知道我在这里达到了界限(也许)。有人知道吗?这在 C++17 中完全可行吗?基本上我想在运行时决定返回什么类型。不使用(明显的)模板。只是 'auto' 和 lambdas。

当然 C++ 不会让我这样做。但即使I use if constexprmsvc 拒绝打球:

    auto msvc_does_not_compile_too = [](auto _string )
{
    using string_type = decltype(_string);

    if constexpr ( std::is_same<string_type, std::string>::value )
          return std::vector<std::string> ;
    else {
        return std::vector<std::wstring>;
    }
};

奇怪的是,我无法在编译时决定运行时需要什么类型。使用 lambda 和 auto,即。

谢谢...

【问题讨论】:

  • 您的标题具有误导性。返回类型必须在编译时已知(当然可以是多态基类std::anystd::variant)。在您的示例中,您使用的是模板化 lambda。关于您的问题:GCC 7.2.0 and Clang 5.0.0 compile your example without any issues.
  • 您是否包含&lt;vector&gt;&lt;string&gt;?包括我的 clang++ 和 g++ 编译你的第一个例子;没有&lt;string&gt; 我会收到类似的错误。
  • @max66:godbolt.org link included in my first comment 显示与 MSVC 19 的问题类似的错误消息。
  • @Julius - 我明白了......好吧,我想是一个 VC++ 错误。
  • 你真的需要尾随返回类型吗?

标签: c++ visual-c++ lambda c++17


【解决方案1】:

在 godbolt.org 的编译器资源管理器中,我发现 MSVC 2017 RTW 19.10.25017 complains about the simplest use of constexpr if 即使带有标志 /std:c++latest。但是,an official blog post 声称自 VS 2017.3 [P2](非 RTW)起支持 constexpr if。您可能应该检查您的 MSVC 版本是否可以使用编译标志 /std:c++latest 处理 constexpr if 的这些简单用法。

如果使用简单的函数模板(如果需要,还可以使用特化)使用简单的重载,你想要实现的效果就很好。

https://godbolt.org/g/BwuSPG

#include <string>
#include <vector>

namespace basic_problem {
    auto now_it_works(const std::string&) {
      return std::vector<std::string>{};
    }

    auto now_it_works(const std::wstring&) {
      return std::vector<std::wstring>{};
    }
}

int main(int argc, char* argv[]) {
  auto str = basic_problem::now_it_works(std::string{});
  static_assert(std::is_same<
    decltype(str), std::vector<std::string>
  >{}, "");

  auto wstr = basic_problem::now_it_works(std::wstring{});
  static_assert(std::is_same<
    decltype(wstr), std::vector<std::wstring>
  >{}, "");

  return 0;
}

作为旁注,工作代码说明您的标题具有误导性:不能在运行时决定返回哪种类型。而是在编译时选择适当的 模板实例化(或特化) 重载。

【讨论】:

  • 谢谢...我在我的问题中已经(可能没有明确)说明我希望有基于 lambda 和 autos 的解决方案...
  • 另外(如果可能的话)API 的核心(似乎 API 设计的质量是关键)非常简单: auto command_line_data = create_command_line_data() ;根据 __wargv 是否为空,我们返回宽字符串或窄字符串的向量。将解决方案全部封装在这个 lambda 中......我们有 gcc 和 clang 但没有 msvc......我不想回到 C++98,因为 MSVC 无法编译合法的 C++17。再次感谢您的参与。
  • @DusanJovanovic:你考虑过template&lt;class T&gt; auto command_line_data(const T&amp; arg) { /* ... */ }吗?我认为这将提供相同的“API”。
  • 非常感谢@Julius ...经过几天的思考,我设法说服 MSVC 允许我做一些可能被称为简单的事情。我明天要去旅行,所以我不能保证在周日之前用代码(我已经完成了)得到完整的答案。
【解决方案2】:

要求:获取表示命令行的向量。对所有可能在 WIN 10 中具有命令行的可执行文件执行此操作。

但我不是在这里让人们做我的“功课”我来这里是为了检查我是否正确使用 C++17 lambda+auto 解决方案。它看起来简单健壮且快速。事实证明它是但不使用 msvc。

实际用例在 MSVC + WIN 情况下对全局变量进行操作

     #include <stdlib.h>
     #define _CRT_DECLARE_GLOBAL_VARIABLES_DIRECTLY
       auto   wargv_ = (__wargv);
       auto    argv_  = (__argv );
       auto    argc_  = (__argc ); 
   #undef _CRT_DECLARE_GLOBAL_VARIABLES_DIRECTLY

到目前为止一切顺利。但是我们如何知道什么时候 wargv_ 会为空,什么时候 argv_ 会为空呢?好像wargv_总是为null,但不能确定。

那么我们如何封装解决方案的核心呢?用一个 lambda。实施几乎是微不足道的。没有重载,也没有模板。只是现代 C++。

    auto command_line_data = [&]() {
      if ( wargv_ != nullptr ) {
        return std::vector<std::wstring>(wargv_, wargv_ + argc_);
      }
       else {
        return std::vector<std::string>(argv_, argv_ + argc_);
      }
};

唉 msvc 不能接受这个...感谢@Julius 对 gcc、clang、msvc 比较进行编码。

更新——“简单”且对 msvc 友好的解决方案

 #include <stdlib.h>
namespace {
#define _CRT_DECLARE_GLOBAL_VARIABLES_DIRECTLY

const auto  wargv_ = (__wargv);
const auto  argv_ = (__argv);
const auto  argc_ = (__argc);

#undef _CRT_DECLARE_GLOBAL_VARIABLES_DIRECTLY

namespace {

    using wvecT = std::vector<std::wstring>;
    using nvecT = std::vector<std::string >;

    // wargv_ !=  nullptr
    inline auto decide(std::true_type tt) {
        return wvecT{ wargv_, wargv_ + argc_ };
    }
    // wargv_ ==  nullptr
    inline auto decide( bool ft) {
        return nvecT{ argv_, argv_ + argc_ };
    }
}

auto cli_data = []() {
    return decide( 
        wargv_ != NULL 
        ? std::true_type{}
        : false
    );
};
} // nspace

我现在没有时间进行比较,但是决定()重载和调用似乎“有趣”,我认为这要归功于 msvc 运行时间......

我相信这可以进行优化,使其看起来不像特技。但这行得通..

VS 15.5 更新后(对我而言:2017-12-05)

直到昨天(2017-12-06),这还没有在 MSVC 下编译。但在 VS 15.5 更新后它确实如此。

    auto msvc_does_compile = [](auto _string)
     {
      using string_type = decltype(_string);
      return std::vector<string_type>{};
     };
   /*
    OK since VS 2017 15.5 update
   auto vec1 = msvc_does_compile( std::string{} );
   */

添加显式返回类型仍会阻塞 MSVC,但不会像往常一样阻塞 gcc/clang:

auto msvc_does_not_compile = [](auto _string)
    // explicit return type makes msvc not to compile
    -> std::vector< decltype(_string) >
  {
    using string_type = decltype(_string);
    return std::vector<string_type>{};
  };

即使在 IDE 编辑器阶段也会停止相同但更简单的操作:

    auto msvc_ide_does_not_allow = []( bool wide )
    {
       if (wide)
        return std::vector<std::string>();

        return std::vector<std::wstring>();
    };

是的,同样麻烦的 gcc/clang 对在上述任何一个方面都没有问题。尝试使用您喜欢的在线 ide 来说服自己...

编辑 01FEB2018

我的解决方案是错误的,这是一个噱头。这是我们目前能做的最好的:

#pragma once

#include <cstdlib>
#include <vector>
#include <string>

#include <tchar.h>

namespace stwish {

    using vector_tstring_type = std::vector<std::basic_string<_TCHAR> >;
    namespace {

        auto cli_data = []() {
            try {
                return
                    vector_tstring_type
                {
                    __targv,
                    (__targv ?
                    __targv + __argc :
                    __targv)
                };
            }
            catch (...) {
                throw std::runtime_error(__FUNCSIG__ " failed..." );
            }
        };
    }
}

这是 Steve Wishnousky 先生(微软)设计的解决方案。谢谢史蒂夫。

【讨论】:

  • wargv_pointer 永远不能是空指针。 if constexpr 将始终采用相同的分支。目前尚不清楚您要做什么。无论如何,这似乎无法回答问题。
  • 所以?您没有检查 wargv_ 的值(我认为您不能这样做,因为它可能不是常量表达式)。您正在检查它的地址(永远不会为空)。
  • 我知道并且我已经更改了代码......我现在没有时间提供完整的实现......也许你可以?
  • 我仍然不知道您要做什么,但显然 MSVC 根本无法编译 constexpr if 和其他一些 C++17 结构。这很不幸,但我们无能为力。
  • @n.m.只是为了让您知道,我感谢您的参与。
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2016-05-06
  • 1970-01-01
  • 1970-01-01
  • 2023-01-28
  • 1970-01-01
相关资源
最近更新 更多