Pruebe mi código usando Travis. Recientemente, alguien agregó GCC9 al conjunto de compiladores con el que se prueba el código. Si bien todo se compila, múltiple con GCC8 (ambos con C ++ 14 como C ++ 17) y GCC-9.1.0 con C ++ 14, falla con GCC-9.1.0 con C ++ 17 con el siguiente error:

/usr/include/c++/9/functional: In instantiation of ‘std::_Bind<_Functor(_Bound_args ...)>::_Bind(const _Functor&, _Args&& ...) [with _Args = {std::tuple<int>}; _Functor = SQLite::Statement; _Bound_args = {std::tuple<int>}]’:
/usr/include/c++/9/functional:811:38:   required from ‘typename std::_Bind_helper<std::__is_socketlike<_Func>::value, _Func, _BoundArgs ...>::type std::bind(_Func&&, _BoundArgs&& ...) [with _Func = SQLite::Statement&; _BoundArgs = {std::tuple<int>}; typename std::_Bind_helper<std::__is_socketlike<_Func>::value, _Func, _BoundArgs ...>::type = std::_Bind<SQLite::Statement(std::tuple<int>)>]’
/home/travis/build/SRombauts/SQLiteCpp/include/SQLiteCpp/ExecuteMany.h:84:9:   required from ‘void SQLite::bind_exec(SQLite::Statement&, std::tuple<_Tps ...>&&) [with Types = {int}]’
/home/travis/build/SRombauts/SQLiteCpp/include/SQLiteCpp/ExecuteMany.h:50:14:   required from ‘void SQLite::execute_many(SQLite::Database&, const char*, Arg&&, Types&& ...) [with Arg = std::tuple<int>; Types = {std::tuple<int, const char*>, std::tuple<int, const char*>}]’
/home/travis/build/SRombauts/SQLiteCpp/tests/ExecuteMany_test.cpp:35:9:   required from here
/usr/include/c++/9/functional:462:59: error: ‘SQLite::Statement::Statement(const SQLite::Statement&)’ is private within this context
  462 |  : _M_f(__f), _M_bound_args(std::forward<_Args>(__args)...)
      |                                                           ^
In file included from /home/travis/build/SRombauts/SQLiteCpp/include/SQLiteCpp/Column.h:13,
                 from /home/travis/build/SRombauts/SQLiteCpp/include/SQLiteCpp/Database.h:13,
                 from /home/travis/build/SRombauts/SQLiteCpp/tests/ExecuteMany_test.cpp:13:
/home/travis/build/SRombauts/SQLiteCpp/include/SQLiteCpp/Statement.h:696:5: note: declared private here
  696 |     Statement(const Statement&);

El código que arroja este error es lo siguiente:

template <typename Arg, typename... Types>
void execute_many(Database& aDatabase, const char* apQuery, Arg&& aArg, Types&&... aParams)
{
    Statement query(aDatabase, apQuery);
    bind_exec(query, std::forward<Arg>(aArg));
    (void)std::initializer_list<int>
    {
        ((void)reset_bind_exec(query, std::forward<Types>(aParams)), 0)...
    };
}

template <typename TupleT>
void reset_bind_exec(Statement& apQuery, TupleT&& aTuple)
{
    apQuery.reset();
    bind_exec(apQuery, std::forward<TupleT>(aTuple));
}

template <typename TupleT>
void bind_exec(Statement& apQuery, TupleT&& aTuple)
{
    bind(apQuery, std::forward<TupleT>(aTuple));
    while (apQuery.executeStep()) {}
}

Uso el siguiente código para Travis CI para usar el compilador correspondiente.

matrix:
  include:
    - compiler: gcc
      addons:
        apt:
          sources:
            - ubuntu-toolchain-r-test
          packages:
            - g++-9
      env:
        - CC=gcc-9
        - CXX=g++-9
        - CXXFLAGS="-std=c++17 -Wall -Wextra -pedantic"

before_install:
  # coveralls test coverage:
  - if [[ "$CXX" == "g++" ]]; then pip install --user cpp-coveralls ; fi

# scripts to run before build
before_script:
  - gcc --version
  - mkdir build
  - cd build
  - cmake -DCMAKE_BUILD_TYPE=Debug -DSQLITECPP_USE_GCOV=ON -DSQLITECPP_BUILD_EXAMPLES=ON -DSQLITECPP_BUILD_TESTS=ON ..

# build examples, and run tests (ie make & make test)
script:
  - cmake --build .
  - ctest --verbose --output-on-failure

La declaración de la clase tiene un constructor de copia privada y un operador de asignación, pero me pregunto por qué esto causaría cualquier problema aquí, porque no copio la declaración "Query". Especialmente por qué este problema solo ocurre con GCC-9.1.0 con C ++ 17 (en mi máquina local, utilizo GCC-9.1.1 y se compila sin ningún error).

1
maxbachmann 26 jun. 2019 a las 08:08

1 respuesta

La mejor respuesta

Un ejemplo más mínimo es este:

#include <functional>

namespace SQLite
{
    struct Statement
    {
        Statement() = default;
        Statement(const Statement&) = delete;
    };
    template<class ...Args>
    void bind( Statement& s, const Args& ... args )
    {        
    }

    template < typename T >
    void test(T&& t)
    {
        Statement s;
        bind( s, std::forward< T >( t ) );
    }    
}

int main() 
{
    std::tuple< int > t;
    SQLite::test( t );
}

Como uno de sus argumentos es de la búsqueda dependiente del argumento del espacio de nombres std en la lista de funciones utilizables. SQLite::bind Requiere una conversión a const& antes de llamar así que std::bind es una mejor coincidencia.

Tienes algunas opciones para arreglar esto:

  1. Llame explícitamente SQLite::bind
  2. Cambie el nombre de bind a algo que no está en la biblioteca estándar (esta puede ser la mejor opción a medida que deje de que sus usuarios se ejecuten en el mismo problema)
  3. Cambiar argumentos bind de const Args& ... args a Args&& ... args para eliminar la conversión
1
Alan Birtles 26 jun. 2019 a las 08:51