在我的项目中,我正在使用Boost.Bimap来实现双向地图。
看看this very simple MCVE on godbolt,我在这里使用structured binding打印右图的键/值对(根据文档,它与std::map
签名兼容。
对于任何> = 7.4及更高版本的g ++版本,它都可以正常编译,但是我需要使用g ++ 7.1。并且此代码在此失败,并显示以下消息:
<source>: In function 'int main()':
<source>:11:20: error: 'std::tuple_size<const boost::bimaps::relation::structured_pair<boost::bimaps::tags::tagged<const long unsigned int, boost::bimaps::relation::member_at::right>, boost::bimaps::tags::tagged<const std::__cxx11::basic_string<char>, boost::bimaps::relation::member_at::left>, mpl_::na, boost::bimaps::relation::mirror_layout>>::value' is not an integral constant expression
for (const auto& [key, value] : bm.right) {
我能够发现g ++中的this is due to a bug在更高版本中似乎已得到修复。
为了使结构化绑定适用于我的编译器版本,我尝试通过专门化std::tuple_size
,std::tuple_element
和std::get
来创建解决方法。有关更多信息,请参见this cppreference link。
为简单起见,我首先用玩具结构成功地尝试了这一方法。以下是专长check out the full code on godbolt.org:
struct SampleType {
int a = 42;
std::string b = "foo"s;
double c = 3.141;
};
#if (__GNUC__ == 7) && (__GNUC_MINOR__ == 1)
template <std::size_t N>
decltype(auto) get(const ::SampleType& t) {
if constexpr (N==0) return t.a;
else if constexpr (N==1) return t.b;
else return t.c;
}
namespace std {
// Tuple size is 3
template <> struct tuple_size<::SampleType> : std::integral_constant<std::size_t, 3> {};
// Define tuple types
template <std::size_t N> struct tuple_element<N, ::SampleType> {
// Deduce type from get() function template defined above
using type = decltype(::get<N>(std::declval<::SampleType>()));
};
}
#endif
请注意,如果您删除了g ++ 7.1。的#ifdef
,编译将失败,并出现与上述相同的错误(...is not an integral constant expression
)。 (有趣的是:与boost::bimap
示例不同,后者仅在g ++ 7.4以后才可以正常编译,玩具示例在g ++ 7.2之前已经可以成功使用)
现在,我非常确信自己找到了解决方案,因此我尝试为boost::bimap
做同样的事情,但是我无能为力(check it out on godbolt.org):
template <std::size_t N>
decltype(auto) get(const bimap::right_map::value_type& bm) {
if constexpr (N==0) return bm.first;
else if constexpr (N==1) return bm.second;
}
namespace std {
// Tuple size is 2 -> key-value pair
template <> struct tuple_size<bimap::right_map::value_type> : std::integral_constant<std::size_t, 2> {};
// Define tuple types
template <> struct tuple_element<0, bimap::right_map::value_type> { using type = std::string; };
template <> struct tuple_element<1, bimap::right_map::value_type> { using type = std::size_t; };
}
错误消息太长了,无法在此处发布(请参见godbolt输出),但是基本上我理解编译器没有匹配“ my” get
的重载。请注意,出于调试的原因,我在代码中插入了以下行,以确保在我的专业领域中实际上正在使用正确的类型。
for (const auto& pair : bm.right) {
// Make sure we capture the right type in the specializations above
static_assert(std::is_same<
decltype(pair),
const bimap::right_map::value_type&
>::value);
}
我做错什么了吗?还是这个错误给我的解决方法带来了无法克服的阻碍?
答案 0 :(得分:1)
我认为这不是您可以解决的问题。
这里的复制时间较短:
#include <tuple>
namespace N {
struct X {
template <typename T> void get() { }
};
}
namespace std {
template <> struct tuple_size<N::X> : integral_constant<size_t, 1> { };
template <> struct tuple_element<0, N::X> { using type = int; };
}
namespace N {
template <size_t I> decltype(auto) get(X const&) { return 42; }
}
int main() {
auto [i] = N::X{};
}
这是一个有效的程序。 [dcl.struct.bind]/4的措辞说,重点是我的:
通过类成员访问查找([basic.lookup.classref])在
E
范围内查找unqualified-id get,如果找到,则至少找到一个声明为函数模板 >其第一个模板参数是非类型参数,则初始化程序为e.get<i>()
。否则,初始化程序为get<i>(e)
,在其中的get在关联的命名空间([basic.lookup.argdep])中查找。
N::X
具有成员函数模板get()
且带有类型模板参数的事实应使我们考虑在get
上进行ADL查找,该查找应找到非成员{ {1}}。 gcc 7.4可以正确执行此操作,gcc 7.3抱怨N::get
无法正常工作。
唯一的解决方法是以某种方式包装初始化程序。基本上是这样的:
N::X::get()
在auto [i] = wrap(N::X{});
返回某些绝对没有名为wrap
的成员的新类型的地方,因此您可以提供所需的非成员。我不确定这里是否有不需要额外包装的解决方案。除了只使用gcc 7.4:-)