Почему GCC говорит о зависимой базе? - PullRequest
0 голосов
/ 05 декабря 2018

Это продолжение Когда виден член класса?

После компиляции класса с помощью GCC, перемещая объявление pk_ в начало, я попытался использоватьэто:

#include <string>
#include <map>
#include <type_traits>

using from_type = std::map<std::string, std::string>;

template<typename PK, size_t N>
struct pf {
public:
    PK pk_;

    pf(from_type const& a, std::string const& pkn) noexcept(noexcept(fill(pk_, std::string{})))
    : map_(a) // GCC 4.8 requires ()s for references
    , pk_{ [&]{ fill(pk_, pkn); return pk_; }() }
    {
    }

    template<typename prop_t>
    typename std::enable_if<
        std::is_integral<typename std::decay<prop_t>::type>::value,
        pf<PK, N>>::type const&
    fill(prop_t& , std::string const& , prop_t  = 0) const noexcept(false);

    pf<PK, N> const&
    fill(std::string& , std::string const&) const noexcept;
protected:
    from_type const& map_;
    uint32_t aieee;

};

std::string k;
from_type m;

int i;
std::string s;

static_assert(!noexcept(pf<int        , 42>{m, k}), "int could throw");
static_assert( noexcept(pf<std::string, 17>{m, k}), "string shouldn't throw");

clang 4.0, 6.0 и trunk снова скомпилировали программу.

GCC все еще не был доволен:

$ g++-99 -Wall -pedantic -Wextra -Wformat=2 -std=c++14 pf.cpp
pf.cpp: In instantiation of ‘pf<PK, N>::pf(const from_type&, const string&) [with PK = int; long unsigned int N = 42; from_type = std::map<std::__cxx11::basic_string<char>, std::__cxx11::basic_string<char> >; std::string = std::__cxx11::basic_string<char>]’:
pf.cpp:38:49:   required from here
pf.cpp:12:74: error: no matching function for call to ‘fill(int&, std::__cxx11::basic_string<char>)’
   12 |     pf(from_type const& a, std::string const& pkn) noexcept(noexcept(fill(pk_, std::string{})))
      |                                                                      ~~~~^~~~~~~~~~~~~~~~~~~~
In file included from /usr/local/lib/gcc/x86_64-pc-linux-gnu/9.0.0/include/c++/bits/char_traits.h:39,
                 from /usr/local/lib/gcc/x86_64-pc-linux-gnu/9.0.0/include/c++/string:40,
                 from pf.cpp:1:
/usr/local/lib/gcc/x86_64-pc-linux-gnu/9.0.0/include/c++/bits/stl_algobase.h:742:5: note: candidate: ‘template<class _ForwardIterator, class _Tp> void std::fill(_ForwardIterator, _ForwardIterator, const _Tp&)’
  742 |     fill(_ForwardIterator __first, _ForwardIterator __last, const _Tp& __value)
      |     ^~~~
/usr/local/lib/gcc/x86_64-pc-linux-gnu/9.0.0/include/c++/bits/stl_algobase.h:742:5: note:   template argument deduction/substitution failed:
pf.cpp:12:74: note:   deduced conflicting types for parameter ‘_ForwardIterator’ (‘int’ and ‘std::__cxx11::basic_string<char>’)
   12 |     pf(from_type const& a, std::string const& pkn) noexcept(noexcept(fill(pk_, std::string{})))
      |                                                                      ~~~~^~~~~~~~~~~~~~~~~~~~
pf.cpp: In instantiation of ‘pf<PK, N>::pf(const from_type&, const string&) [with PK = std::__cxx11::basic_string<char>; long unsigned int N = 17; from_type = std::map<std::__cxx11::basic_string<char>, std::__cxx11::basic_string<char> >; std::string = std::__cxx11::basic_string<char>]’:
pf.cpp:39:49:   required from here
pf.cpp:12:74: error: no matching function for call to ‘fill(std::__cxx11::basic_string<char>&, std::__cxx11::basic_string<char>)’
In file included from /usr/local/lib/gcc/x86_64-pc-linux-gnu/9.0.0/include/c++/bits/char_traits.h:39,
                 from /usr/local/lib/gcc/x86_64-pc-linux-gnu/9.0.0/include/c++/string:40,
                 from pf.cpp:1:
/usr/local/lib/gcc/x86_64-pc-linux-gnu/9.0.0/include/c++/bits/stl_algobase.h:742:5: note: candidate: ‘template<class _ForwardIterator, class _Tp> void std::fill(_ForwardIterator, _ForwardIterator, const _Tp&)’
  742 |     fill(_ForwardIterator __first, _ForwardIterator __last, const _Tp& __value)
      |     ^~~~
/usr/local/lib/gcc/x86_64-pc-linux-gnu/9.0.0/include/c++/bits/stl_algobase.h:742:5: note:   template argument deduction/substitution failed:
pf.cpp:12:74: note:   candidate expects 3 arguments, 2 provided
   12 |     pf(from_type const& a, std::string const& pkn) noexcept(noexcept(fill(pk_, std::string{})))
      |                                                                      ~~~~^~~~~~~~~~~~~~~~~~~~
pf.cpp:39:16: error: static assertion failed: string shouldn't throw
   39 | static_assert( noexcept(pf<std::string, 17>{m, k}), "string shouldn't throw");
      |                ^~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~

Это сбивало с толку (кто-то сказал что-нибудьпро итераторы?) пока не увидел ответ на свой первый вопрос.Компилятор не мог видеть члены fill, поэтому он попробовал единственный доступный ему метод fill: метод из <algorithm>, который был случайно включен через

. /usr/local/lib/gcc/x86_64-pc-linux-gnu/9.0.0/include/c++/string
.. /usr/local/lib/gcc/x86_64-pc-linux-gnu/9.0.0/include/c++/bits/char_traits.h
... /usr/local/lib/gcc/x86_64-pc-linux-gnu/9.0.0/include/c++/bits/stl_algobase.h

Так что я переименовалfill членов для fillz (включая оператор в noexcept), что привело к:

$ g++-99 -Wall -pedantic -Wextra -Wformat=2 -std=c++14 pf.cpp
pf.cpp: In instantiation of ‘pf<PK, N>::pf(const from_type&, const string&) [with PK = int; long unsigned int N = 42; from_type = std::map<std::__cxx11::basic_string<char>, std::__cxx11::basic_string<char> >; std::string = std::__cxx11::basic_string<char>]’:
pf.cpp:38:49:   required from here
pf.cpp:12:75: error: ‘fillz’ was not declared in this scope, and no declarations were found by argument-dependent lookup at the point of instantiation [-fpermissive]
   12 |     pf(from_type const& a, std::string const& pkn) noexcept(noexcept(fillz(pk_, std::string{})))
      |                                                                      ~~~~~^~~~~~~~~~~~~~~~~~~~
pf.cpp:12:75: note: declarations in dependent base ‘pf<int, 42>’ are not found by unqualified lookup
pf.cpp:12:75: note: use ‘pf::fillz’ instead
pf.cpp:12:75: error: cannot call member function ‘const typename std::enable_if<std::is_integral<typename std::decay<prop_t>::type>::value, pf<PK, N> >::type& pf<PK, N>::fillz(prop_t&, const string&, prop_t) const [with prop_t = int; PK = int; long unsigned int N = 42; typename std::enable_if<std::is_integral<typename std::decay<prop_t>::type>::value, pf<PK, N> >::type = pf<int, 42>; std::string = std::__cxx11::basic_string<char>]’ without object
pf.cpp: In instantiation of ‘pf<PK, N>::pf(const from_type&, const string&) [with PK = std::__cxx11::basic_string<char>; long unsigned int N = 17; from_type = std::map<std::__cxx11::basic_string<char>, std::__cxx11::basic_string<char> >; std::string = std::__cxx11::basic_string<char>]’:
pf.cpp:39:49:   required from here
pf.cpp:12:75: error: ‘fillz’ was not declared in this scope, and no declarations were found by argument-dependent lookup at the point of instantiation [-fpermissive]
pf.cpp:12:75: note: declarations in dependent base ‘pf<std::__cxx11::basic_string<char>, 17>’ are not found by unqualified lookup
pf.cpp:12:75: note: use ‘pf::fillz’ instead
pf.cpp:12:75: error: cannot call member function ‘const pf<PK, N>& pf<PK, N>::fillz(std::string&, const string&) const [with PK = std::__cxx11::basic_string<char>; long unsigned int N = 17; std::string = std::__cxx11::basic_string<char>]’ without object
pf.cpp:39:16: error: static assertion failed: string shouldn't throw
   39 | static_assert( noexcept(pf<std::string, 17>{m, k}), "string shouldn't throw");
      |                ^~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~

Очевидно, что GCC все еще не может видеть участника (теперь называется fillz), нопочему он жалуется на зависимые основания?Struct pf не имеет ничего общего с наследованием.Может ли это быть из-за того, что проблема видимости чаще всего возникает с зависимыми базами?

В конце концов, правильное использование оказалось:

noexcept(noexcept(std::declval<pf&>().fill(std::declval<PK&>(), std::string{})))

1 Ответ

0 голосов
/ 05 декабря 2018

На первый взгляд кажется, что в GCC может быть несколько ошибок.

(Все стандартные кавычки взяты из C ++ 14, так как вы компилируете с -std=c++14. Добавлена ​​вся жирность.)

[basic.scope.class] *1

Потенциальная область действия имени, объявленного в классе, состоит из [...] ивсе тела функций, аргументы по умолчанию, спецификации исключений и инициализаторы фигурных или равных скобок нестатических элементов данных в этом классе (включая такие вещиво вложенных классах).

Таким образом, в предложении noexcept объявления функции-члена все члены класса находятся в области видимости независимо от того, в каком порядке они объявлены.

Но этого недостаточно, чтобы убедить себя, что ваш код правильно сформирован.Мы находимся в шаблоне класса, поэтому далее нам нужно рассмотреть двухфазный поиск:

[temp.dep] ¶1

В выражении вида:

postfix-expression ( expression-listopt )

где postfix-expression безусловный идентификатор , безусловный идентификатор обозначает зависимое имя , если

  • (1.1) любое из выражений в выражении-списке является расширением пакета,
  • (1.2) любое выражение в выражении-списке являетсязависимое от типа выражение или
  • (1.3), если unqualified-id является template-id , в котором любой из аргументов шаблона зависит отпараметр шаблона.

Такие имена не связаны и ищутся в точке создания шаблона как в контексте определения шаблона, так и в контексте точки создания.

Поэтому fill является зависимым именем в fill(pk_, std::string{}), поскольку pk_ зависит от типа, поэтому при создании шаблона будет выполняться поиск fill, оба в контексте шаблона.сам и точка, в которой он был создан.Из-за ADL и того факта, что одним из аргументов является std::string, пространство имен std включено в поиск, что приводит к сообщениям о std::fill.Но поиск также должен найти функцию-член fill, как обсуждалось ранее.

Теперь мы убедились, что код правильно сформирован.Мы могли бы просто сообщить об ошибке и двигаться дальше.Но давайте продолжим копаться, чтобы увидеть, насколько хорошо мы понимаем, что происходит.


[temp.dep] ¶3

В определении класса или шаблона класса, если базовый класс зависит от шаблона-параметра , область действия базового класса не проверяется при поиске неквалифицированного имени ни в точке определения шаблона класса, ни в члене, ни во время создания экземпляра класса.шаблон класса или член.

Сообщения об ошибках, по-видимому, указывают на то, что GCC (ошибочно) считает включающий класс fill зависимой базой fill и, следовательно, применяет формулировку[temp.dep] ¶3.

Обычный способ ссылаться на имена в зависимых базах - обращаться к ним через квалифицированный идентификатор (например, pf::fill) или член классавыражение доступа (например, this->fill).Итак, что происходит, когда мы пытаемся использовать любой из этих подходов в качестве обходного пути?

Запись this->fill, кажется, работает в вашем примере кода, но, как отмечено @nm в комментариях, это хрупко и не работаетв более минимальном примере - ошибка «недопустимое использование this на верхнем уровне».

Запись pf::fill приводит к ошибке «невозможно вызвать функцию-член без объекта».Это, вероятно, по той же причине, по которой произошел сбой this->fill: если this недействителен, то он также должен быть недействительным для преобразования id-выражения в неявное выражение доступа к члену.

[expr.prim.general] / 3

Если объявление объявляет функцию-член или шаблон функции-члена класса X, выражение this является указателем типа типа "на cv-qualifier-seq X " между необязательным cv-qualifer-seq и концом определения-функции , -declarator , или декларатор .

Так что this действительно для появления в спецификации исключений , и GCC неправильно отклоняет егои это выглядит на первый взгляд как ошибка, отличная от той, которую мы уже диагностировали.Этот последний уже известен как 52869 , но тестовый пример, представленный с исправлением, не смог выполнить случай, когда noexcept появляется перед объявлением функции-члена, на которую он ссылается, как в вашем случае.Поэтому неясно, действительно ли ваш первый набор ошибок является частью той же ошибки.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...