std :: get on std :: tuple导致variadic args减少到没有不完整类型错误

时间:2017-07-23 18:50:50

标签: c++11 gcc variadic-templates stdtuple

以下代码会产生大量编译器错误:

/// Uses template recursion to bind all args
template<std::size_t N, typename... Args> class Binder
{
public:
    Binder(Sqlite3StatementBase &s, std::tuple<Args...> &tup)
    {
        Binder<N - 1, Args...> b(s, tup);
        s.bind(N + 1, std::get<N, Args...>(tup)); // Line 182
    }
};

/// Specialization of Binder to end recursion at 0
template<typename... Args> class Binder<0, Args...>
{
public:
    Binder(Sqlite3StatementBase &s, std::tuple<Args...> &tup)
    {
        s.bind(1, std::get<0, Args...>(tup));
    }
};

第一批错误包括:

In file included from /usr/include/c++/6/bits/unique_ptr.h:37:0,
             from /usr/include/c++/6/condition_variable:43,
             from /home/tony/htpc/Dev/logi/src/db/logi-db.h:22,
             from /home/tony/htpc/Dev/logi/src/db/logi-sqlite.h:26,
             from /home/tony/htpc/Dev/logi/src/db/logi-sqlite.cpp:24:
/usr/include/c++/6/tuple: In instantiation of ‘class std::tuple_element<1ul, std::tuple<unsigned int> >’:
/usr/include/c++/6/tuple:1228:12:   recursively required from ‘class std::tuple_element<2ul, std::tuple<unsigned int, unsigned int> >’
/usr/include/c++/6/tuple:1228:12:   required from ‘class std::tuple_element<3ul, std::tuple<unsigned int, unsigned int, unsigned int> >’
/usr/include/c++/6/utility:106:69:   required by substitution of ‘template<long unsigned int __i, class _Tp> using __tuple_element_t = typename std::tuple_element::type [with long unsigned int __i = 3ul; _Tp = std::tuple<unsigned int, unsigned int, unsigned int>]’
/usr/include/c++/6/tuple:1270:5:   required by substitution of ‘template<long unsigned int __i, class ... _Elements> constexpr std::__tuple_element_t<__i, std::tuple<_Elements ...> >&& std::get(std::tuple<_Elements ...>&&) [with long unsigned int __i = 3ul; _Elements = {unsigned int, unsigned int, unsigned int}]’
/home/tony/htpc/Dev/logi/src/db/logi-sqlite.h:182:47:   required from ‘logi::Sqlite3Database::Binder<N, Args>::Binder(logi::Sqlite3Database::Sqlite3StatementBase&, std::tuple<_Elements ...>&) [with long unsigned int N = 3ul; Args = {unsigned int, unsigned int, unsigned int}]’
/home/tony/htpc/Dev/logi/src/db/logi-sqlite.h:238:71:   required from ‘void logi::Sqlite3Database::Sqlite3Statement<Args>::prepare_row(logi::Sqlite3Database::Sqlite3Statement<Args>::Tup&) [with Args = {unsigned int, unsigned int, unsigned int}; logi::Sqlite3Database::Sqlite3Statement<Args>::Tup = std::tuple<unsigned int, unsigned int, unsigned int>]’
/home/tony/htpc/Dev/logi/src/db/logi-sqlite.h:231:28:   required from ‘void logi::Sqlite3Database::Sqlite3Statement<Args>::execute(typename logi::Sqlite3Database::Sqlite3Statement<Args>::Parent::ArgsVector&) [with Args = {unsigned int, unsigned int, unsigned int}; typename logi::Sqlite3Database::Sqlite3Statement<Args>::Parent::ArgsVector = std::vector<std::tuple<unsigned int, unsigned int, unsigned int>, std::allocator<std::tuple<unsigned int, unsigned int, unsigned int> > >]’
/home/tony/htpc/Dev/logi/src/db/logi-sqlite.cpp:354:1:   required from here
/usr/include/c++/6/tuple:1228:12: error: invalid use of incomplete type ‘class std::tuple_element<0ul, std::tuple<> >’

所以似乎std::get调用导致系统库的其他部分(与std::getstd::tuple没有直接关系)以递归方式将Args ...减少为空,这与我对数字模板参数N的递归无关。我可以在我的代码中修改一些内容而不会从根本上改变我的方法,或者在元组和#的上下文中使用std::get是不可能的39; s模板参数是可变参数吗?

1 个答案:

答案 0 :(得分:1)

给定{+ 3} C ++ 14 make_index_sequence函数(及其相关类型),你可以轻松地做你想做的事情:

template<size_t ...indices, typename ...Args>
void binder_helper(Sqlite3StatementBase &s, std::integer_sequence<size_t, indices...>, std::tuple<Args...> &tup)
{
    auto dump = {(s.bind(indices, std::get<indices>(tup)), 0)...};
}

template<typename ...Args>
void binder(Sqlite3StatementBase &s, std::tuple<Args...> &tup)
{
    binder_helper(s, std::make_index_sequence<sizeof...(Args)>(), tup);
}

使用C ++ 17的折叠表达式,binder_helper中的奇怪东西可以简化为更合理的东西:

template<size_t ...indices, typename ...Args>
void binder_helper(Sqlite3StatementBase &s, std::integer_sequence<size_t, indices...>, std::tuple<Args...> &tup)
{
    (s.bind(indices, std::get<indices>(tup)), ...);
}

请注意,在后一种情况下,标准保证将按顺序执行对s.bind的调用。在前一种情况下,由于表达式包含在braced-init-list中,因此您可以保证按顺序进行评估。