【问题标题】:std::tuple duplicate type T in get<T>(tuple) - Compile time assertion failurestd::tuple 在 get<T>(tuple) 中重复类型 T - 编译时断言失败
【发布时间】:2019-07-10 21:57:05
【问题描述】:

我将可变参数存储到 std::tuple 内的对象构造函数中,到目前为止一切顺利。但是当使用存储的参数和std::get&lt;&gt;() 调用对象函数时,我将被抛出一个我根本不理解的compile-time 断言失败。这只有在所有参数都不是不同类型时才会发生

编译器错误信息是:

msvc\14.16.27023\include\tuple(934): 错误 C2338: 重复类型 T in 获取(元组)

下面的mcve:

#include <tuple>
#include <iostream>

using namespace std;

template<class... Args>
struct store_in_tuple {

    tuple<Args...> m_tuple_args;

    store_in_tuple(Args... args) : m_tuple_args{ args... } {}

    void func() {
        func_tuple(std::get<Args>(m_tuple_args)...);
    }

    void func_tuple(Args... args) {}
};

int main(int argc, char** argv) {

    store_in_tuple<int, float, double, int> sit1(1, 2.0f, 3.0, 4);
    sit1.func(); // <- not ok

    store_in_tuple<int, float, double, size_t> sit2(1, 2.0f, 3.0, 4);
    sit2.func(); // <- ok

    return 0;
}

为什么会发生这种情况,是否有解决方法?

【问题讨论】:

    标签: c++ windows c++14 variadic-templates stdtuple


    【解决方案1】:

    示例可以简化为:

    auto t = std::make_tuple(1, 's', 2);
    std::get<int>(t);
    

    在这里,我们有一个t 类型为std::tuple&lt;int, char, int&gt;std::get 也可以使用类型(与索引一起),除非您有重复的类型。 std::get&lt;char&gt; 可以工作,因为在t 中只有一个char,但std::get&lt;int&gt; 不会工作,因为它不知道哪个 int 要获取-1还是2

    这就是这里发生的事情:

    void func() {
        func_tuple(std::get<Args>(m_tuple_args)...);
    }
    

    如果Args... 包含至少一种重复类型,则std::get&lt;Args&gt; 在扩展后将不起作用,因为它根本不知道要获取哪个

    【讨论】:

      【解决方案2】:

      使用C++17 std::apply() 将元组的所有元素传递给函数。

      std::apply([&](auto... x){ func_tuple(x...); }, m_tuple_args);
      

      你坚持使用 C++14?
      没问题,cppreference.com 展示了一个简短的生产质量示例实现。

      或者,您可以直接使用std::make_index_sequence 来获取唯一索引而不是重复类型。

      【讨论】:

      • 它使用辅助函数和index_sequence_for 对我有用。谢谢!我认为当将std::apply 用于类实例方法时,必须将实际实例添加到元组中,以便它成为函数/可调用的第一个参数 - 但我负担不起额外的副本;)
      【解决方案3】:

      为什么会发生这种情况 [?]

      Args... 类型完全不同时,一切顺利。

      当类型冲突时你会得到一个错误。

      这是因为std::get&lt;T&gt;(tuple_val),其中T 是一种类型,“除非元组恰好具有该类型的一个元素,否则无法编译”(您可以在this page 中阅读)。这在我看来是合理的。

      一切顺利

      store_in_tuple<int, float, double, size_t> 
      

      因为所有类型都是不同的,你会得到一个错误

      store_in_tuple<int, float, double, int>
      

      因为对std::get&lt;int&gt;(m_tuple_args) 的两次调用都失败了。

      是否有解决方法?

      使用std::get() 的数字版本,它永远可用,当类型冲突时也是如此。

      C++14 中的常用方法是通过带有std::index_sequencestd::make_index_sequence(或std::index_sequence_for)的辅助函数。

      看似复杂,其实很简单

      template <std::size_t ... Is>
      void func_helper (std::index_sequence<Is...> const)
       { func_tuple(std::get<Is>(m_tuple_args)...); }
      
      void func ()
       { func_helper(std::index_sequence_for<Args...>{}); }
      

      如果你可以使用 C++17,你可以使用 std::apply(),那(我想)在后台使用 std::index_sequence

      【讨论】:

      • 谢谢,这为我阐明了 std::index_sequence 的使用。我不想使用辅助函数,所以我将使用 C++17 的 apply 或者....你使用了 std::get() 的 数字版本,这是什么意思我可以在 C++14 中用它解开参数包吗?谢谢!编辑:只需阅读文档无需回答:]
      • @PinkTurtle - 表示“std::get() 的数字版本”我的意思是std::get&lt;&gt;() 的版本接收std::size_t,而不是类型,作为模板参数(从(1)到(4) 在this page。我不完全理解您的以下问题,但您可以看到在 C++11(和更新版本)中解压缩参数包的方法是常见的:您可以在 func_helper() 中看到它.这里的问题是生成索引的参数包;您需要std::make_index_sequencestd::index_sequence_for(几乎相同)。(继续)
      • @PinkTurtle - (继续)不幸的是,std::index_sequence 仅从 C++14 开始可用,我看不到在不定义辅助函数的情况下生成索引序列的方法。从 C++17 开始,您可以使用负责生成索引序列的std::apply(),并使用数字版本的std::get&lt;&gt;()
      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2019-06-15
      • 2019-09-11
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多