This is a continuation of When is a class member visible?
After making the class compile with GCC by moving the declaration of pk_ to the beginning, I tried to use it:
#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 and trunk again compiled the program.
GCC was still not happy:
$ 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");
| ^~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
This was confusing (who said anything about iterators?) until I saw the answer to my first question. The compiler couldn't see the fill members, so it tried the only fill method available to it: the one from <algorithm>, which was accidentally included via
. /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
So I renamed the fill members to fillz (including the one in the noexcept operator), which resulted in:
$ 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");
| ^~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
Obviously GCC can still not see the member (now named fillz), but why does it complain about dependent bases? Struct pf has nothing to do with inheritance. Could it be because this kind of visibility problem comes up most often with dependent bases?
In the end, the correct usage turned out to be:
noexcept(noexcept(std::declval<pf>().fill(pk_, std::string{})))
In fact, the constructor always calls the second version of fill (whose first parameter is a string), which is noexcept. So the computed noexcept was unnecessary :(
Aucun commentaire:
Enregistrer un commentaire