【发布时间】:2018-12-05 14:14:24
【问题描述】:
这是When is a class member visible?的延续
通过将pk_的声明移到开头使类使用GCC编译后,我尝试使用它:
#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{})))
【问题讨论】:
-
我认为你的猜测是正确的。这只是关于这种错误的一个可能原因的注释。这并不意味着您的具体示例因此而失败。
-
std::declval<pf&>(),使用右值似乎是错误的。 -
gcc 说由于 ADL 的依赖基础。它使用 std::basic_string 范围及其基本范围进行查找。
-
顺便说一句,如果你把
fill放在构造函数之前,你会得到另一个有趣的错误:“cannot call fill without object”。 -
你确定你的修复是正确的吗? ideone.com/0BBmqz