【问题标题】:Unable to deduce 'auto' from 'tuple_cat'无法从 'tuple_cat' 推断出 'auto'
【发布时间】:2017-06-22 22:21:16
【问题描述】:

基础问题

我要解决的基本问题是:

我有一个模板参数包 ArgTypes,我需要用 std::optional 中包装的每个类型创建一个元组。例如:make_optional_tuple<int, double, std::string> 应该返回一个 std::tuple<std::optional<int>, std::optional<double>, std::optional<std::string>> 类型的元组,元组中的每个元素都初始化为 std::nullopt

到目前为止我的工作

我正在使用 GCC 7.1 中包含的 g++。我已经与 C++ 类型系统搏斗了很长一段时间,并且我的代码适用于一种类型但不适用于多种类型。我在参数包中使用多种类型得到的错误是:

error: unable to deduce 'auto' from 'tuple_cat<std::make_tuple(_Elements&& ...) [with _Elements = {std::optional<int>&}](), optional_tuple>'

有谁知道我该如何解决这个问题?直觉上(尽管我可能不正确)我认为问题在于 C++ 类型系统无法推断出 auto optional_tuple 的类型,因为它涉及完全解析由参数包生成的不同函数模板的递归链——一些可能是类型系统在尝试解析 auto 变量的类型时无法做到的。

这是一个最小的工作示例:

#include <optional>
#include <tuple>

template<int n, typename ArgType, typename... ArgTypes>
struct make_optional_tuple_helper {
    static auto tuple() {
        std::optional<ArgType> optional_arg = {};
        auto optional_tuple = make_optional_tuple_helper<n-1, ArgTypes...>::tuple();
        return std::tuple_cat<std::make_tuple(optional_arg), optional_tuple>;
    }
};

template<typename ArgType>
struct make_optional_tuple_helper<1, ArgType> {
    static std::tuple<std::optional<ArgType>> tuple() {
        std::optional<ArgType> optional_arg = {};
        return std::make_tuple(optional_arg);
    }
};

template<typename... ArgTypes>
auto make_optional_tuple() {
    return make_optional_tuple_helper<std::tuple_size<std::tuple<ArgTypes...>>::value, ArgTypes...>::tuple();
};

int main() {
    auto i = make_optional_tuple<int>(); // works!
    auto j = make_optional_tuple<int, double>(); // error: unable to deduce 'auto'...
}

(使用g++-7 -std=c++1z example.cpp编译)

感谢您的时间和/或帮助!

【问题讨论】:

    标签: c++ variadic-templates optional template-specialization c++17


    【解决方案1】:

    方式想太多了:

    template<typename... ArgTypes>
    auto make_optional_tuple() {
        return std::tuple<std::optional<ArgTypes>...>{};
    }
    

    由于默认构造的可选项是 nullopt,这就是您所需要的。


    您的具体问题是您使用了错误的括号:

    return std::tuple_cat<std::make_tuple(optional_arg), optional_tuple>;
                        ~~~                                           ~~~
    

    那些应该是括号。照原样,您返回的是指向格式错误的函数模板特化的指针,而不是元组。

    【讨论】:

    • 感谢您的回答。确实这就是问题所在。我会说与其说是“过度思考”,不如说是“对参数包了解不够”。不过,可以肯定的是,你在这里写的东西比我的烂摊子要干净得多。我试图理解这里的语法——特别是std::optional&lt;ArgTypes&gt;... 部分。我见过的所有示例在模板参数名称之后都有省略号,例如ArgTypes...。你介意我让你再解释一下语法吗?
    【解决方案2】:

    它不起作用,因为函数的返回类型是在第一次返回时推断出来的,并且您试图在第一次返回之前调用该函数。

    我相信你可以做一些很长的事情:

    template<typename... ArgTypes>
    struct make_optional_tuple_helper {
        static auto tuple() {
            return std::make_tuple(std::optional<ArgTypes>()...);
        }
    };
    

    或者

    template<typename... ArgTypes>
    struct make_optional_tuple_helper {
        static auto tuple() {
            return std::tuple<std::optional<ArgTypes>...>();
        }
    };
    

    【讨论】:

    • 感谢您的回答。您能否详细说明“在第一次返回时推断”是什么意思?我理解的方式是设置递归的方式是在返回任何语句之前调用堆栈一直遍历到最终的模板专业化(即make_optional_tuple_helper&lt;1, ArgType&gt;);并且这种基本情况的返回类型是可推导出的,因为第一个示例(参数包中有一个类型)有效。
    • 对,我错过了
    • 我明白了,你调用的是 tuple_cat 而不是 tuple_cat()
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 2021-02-08
    • 2016-05-09
    • 2023-03-19
    • 1970-01-01
    • 1970-01-01
    • 2014-12-27
    • 1970-01-01
    相关资源
    最近更新 更多