【问题标题】:Expanding variadic types into another将可变参数类型扩展为另一种
【发布时间】:2018-01-20 21:55:54
【问题描述】:

我正在尝试通过执行类似的操作将一组可变参数类型扩展为另一种类型。

我有一个简单的特征,当TFunction 返回TReturn 时启用。

template<typename TReturn, typename TFunction>
using ReturnsType = enable_if_t<is_same<decltype(declval<TFunction>()()), 
                                TReturn>::value>;

它在所有主要编译器(VC++、GCC、CLANG)中都能完美运行。

现在我想用这个符号创建另一个:

template<typename TReturn, typename... TFunctions>
using AllReturnsType = ???

所以只有在所有传递的函数 (TFunctions...) 都返回 TReturn 时才会启用它。

所以我尝试了这个实现:

template<typename TReturn, typename... TFunctions>
using AllReturnsType = void_t<ReturnsType<TReturn, TFunctions>...>;

但在 VC++ 中失败(在 GCC 和 CLANG 中没问题)

代码游乐场可以在这里找到:https://godbolt.org/g/d839wp

这是 MSVC++ 中的错误吗?如何让它在所有编译器中工作?

谢谢!

编辑:我正在与 MSVC 团队成员联系,他们正在处理它!这是一个已确认的错误。

【问题讨论】:

    标签: c++ templates variadic-templates sfinae c++17


    【解决方案1】:

    这是 MSVC++ 中的错误吗?

    是的,我想是的。

    我怎样才能让它在所有编译器中工作?

    不要问我为什么,但如果你通过一个助手 structenable_if_returns 如下所示

    template <typename T, typename F, 
              bool = std::is_same_v<T, get_return_type<F>>>
    struct enIfR
    { };
    
    template <typename T, typename F>
    struct enIfR<T, F, true>
    { using type = T; };
    
    template <typename T, typename F>
    using enable_if_returns = typename enIfR<T, F>::type;
    

    您的代码也在 MSVC 中编译。

    下面是一个完整的编译(MSVC也是)例子

    #include <iostream>
    #include <type_traits>
    
    template <typename Function>
    using get_return_type = decltype(std::declval<Function>()());
    
    template <typename T, typename F,
             bool = std::is_same_v<T, get_return_type<F>>>
    struct enIfR
     { };
    
    template <typename T, typename F>
    struct enIfR<T, F, true>
     { using type = T; };
    
    template <typename T, typename F>
    using enable_if_returns = typename enIfR<T, F>::type;
    
    template<typename T, typename Function>
    using enable_if_returns0 = typename std::enable_if<
       std::is_same<T, get_return_type<Function>>::value>::type;
    
    template<typename T, typename... Functions>
    using enable_if_all_returns = std::void_t<typename enIfR<T, Functions>::type...>;
    
    int main()
    {
        constexpr auto fint = []() -> int { return 1; };
        constexpr auto fstring = []() -> std::string { return "asd"; };
    
        (enable_if_returns<int, decltype(fint)>)0; // ok
        //(enable_if_returns<int, decltype(fstring)>)0; // fail on all (expected)
        //(enIfR<int, decltype(fstring)>::type)0; // fail on all (expected)
    
        (enable_if_all_returns<int, decltype(fint)>)0; // fail in MSVC (NOT expected, 'get_return_type<unknown-type>' being compiled)
        //(enable_if_all_returns<int, decltype(fint), decltype(string)>)0; // fail in all (expected)
    
        return 0;
    }
    

    【讨论】:

    • 您好,非常感谢您的回复。不幸的是,代理结构解决方案在我的真实代码中不起作用(我为 POC 简化了很多)。我向 MSVC 报告了这个错误,但是……真丢脸!这是 C++11 的东西!
    • @IvanSanz:我同意这似乎是一个错误,但如果您使用std::result_of,那么它似乎可以工作(不幸的是,MSVC 似乎还不支持 std::invoke_result) :Demo
    • @AndyG - 有趣。恕我直言,您应该将此观察结果(和演示代码)作为答案。
    • @IvanSanz - 抱歉,但是...您尝试过 AndyG 的解决方案吗?如果可行...恕我直言,比我的要好得多。
    【解决方案2】:

    我同意max66 的观点,这似乎是一个 MSVC 错误。如果我不得不猜测发生了什么,我会说这可能归结为 MSVC 仍然不完全支持表达式 SFINAE。

    不过,似乎they've been making steady improvements,他们对std::result_of 的实现被宣传为使用它。

    巧合的是,您想通过get_return_type 实现的目标:

    template<typename Function>
    using get_return_type = decltype(declval<Function>()());
    

    已经使用std::result_of(已弃用)和std::invoke_result

    由于最新的 MSVC 似乎缺乏对 std::invoke_result 的支持,看来如果我们修改您的代码以改用 std::result_of,它就可以正常编译:

    template<class Function>
    using get_return_type = typename std::result_of<Function()>::type;
    

    Demo

    【讨论】:

      猜你喜欢
      • 2019-10-25
      • 2020-12-30
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2012-03-27
      • 1970-01-01
      • 1970-01-01
      • 2017-07-23
      相关资源
      最近更新 更多