std::tuple get<T>(tuple) 中的重复类型 T - 编译时断言失败

std::tuple duplicate type T in get<T>(tuple) - Compile time assertion failure

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

编译器错误信息为:

msvc.16.27023\include\tuple(934): error C2338: duplicate type T in get(tuple)

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;
}

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

例子可以简化为:

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

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

这就是这里发生的事情:

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

扩展后的 std::get<Args> 如果 Args... 包含至少一个重复类型,则将不起作用,因为它 不知道 哪个 一个要取.

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

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

您坚持使用 C++14 吗?
没问题,cppreference.com 显示一个简短的 production-quality example-implementation.

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

Why does this happen [?]

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

类型冲突时会出现错误。

这是因为 std::get<T>(tuple_val),其中 T 是一种类型,"Fails to compile unless the tuple has exactly one element of that type"(您可以在 this page 中阅读)。这对我来说似乎很合理。

所以一切顺利

store_in_tuple<int, float, double, size_t> 

因为所有的类型都不同,你从

得到一个错误
store_in_tuple<int, float, double, int>

因为对std::get<int>(m_tuple_args)的两次调用都失败了。

and is there a workaround ?

使用 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