【问题标题】:compiler error: is private within this context only on gcc9 with c++17编译器错误:在此上下文中是私有的,仅在带有 c++17 的 gcc9 上
【发布时间】:2019-06-26 05:08:41
【问题描述】:

我使用 travis 测试我的代码。最近有人将 gcc9 添加到测试代码的编译器集中。虽然使用 gcc8(使用 c++14 和 c++17)和使用 c++14 的 gcc-9.1.0 都可以正常编译,但使用 c++17 的 gcc-9.1.0 会失败,并出现以下错误:

/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&);

引发此错误的代码如下:

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()) {}
}

我用下面的代码让travis CI使用对应的编译器

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

Statement 类有一个私有的复制构造函数和赋值运算符,但我想知道为什么这会在这里引起任何问题,因为我没有复制 Statement“查询”。特别是为什么这个问题只发生在带有 c++17 的 gcc-9.1.0 上(在我的本地机器上,我使用 gcc-9.1.1 并且它编译没有任何错误)。

【问题讨论】:

  • 请显示minimal reproducible examplebind_exec 是否应该使用 std::bind 或其他名为 bind 的函数?如果它没有摆脱using namespace std,你就有了地方。如果它应该是std::bind,无论如何都要摆脱using namespace std
  • 这是 sqlite3 c 库的包装器。稍后我将不得不努力创建一个最小的可重现示例,因为我自己无法重现它(使用 travis CI 使用 gcc 9.1.0 构建时会出现错误,但使用 gcc 9.1.1 不会在本地构建)。 (源代码在github.com/maxbachmann/SQLiteCpp)。令人困惑的是,实际上它不应该需要 Statement 类的复制构造函数,即使它会失败,例如在其他版本的 gcc 中也是如此。这是相应的 travis 构建:travis-ci.org/SRombauts/SQLiteCpp/builds/549915185
  • 在所有版本的 clang 和 gcc 上快速破解 Godbolt 失败:godbolt.org/z/Qlya1l,将 bind 更改为 SQLite::bind 修复它,std::bind 出于某种原因被 ADL 选中比SQLite::bind
  • 哇需要尝试一下(你知道为什么会发生这种情况,因为我不使用命名空间标准)

标签: c++ c++17 travis-ci gcc9


【解决方案1】:

一个更简单的例子是这样的:

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

由于您的参数之一来自std 命名空间,因此参数依赖查找将std::bind 带入可用函数列表。 SQLite::bind 需要在调用之前转换为 const&amp;,所以 std::bind 是更好的匹配。

您有几个选项可以解决此问题:

  1. 明确调用SQLite::bind
  2. bind 的名称更改为不在标准库中的名称(这可能是最好的选择,因为它可以防止您的用户遇到同样的问题)
  3. bind 的参数从 const Args&amp; ... args 更改为 Args&amp;&amp; ... args 以删除转换

【讨论】:

    猜你喜欢
    • 2017-08-26
    • 2022-08-04
    • 2012-08-13
    • 1970-01-01
    • 1970-01-01
    • 2019-07-04
    • 2017-09-04
    • 2013-04-27
    • 2020-04-26
    相关资源
    最近更新 更多