在我的项目中,我使用Boost.Bimap实现双向映射。
看看这个非常简单的MCVE在哥德螺栓上,在这里,我使用结构化绑定打印正确映射的键值对(根据文档,它与std::map
兼容签名。
问题
它可以很好地编译任何g++版本的>= 7.4及更高版本,但是我需要使用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++中似乎已经在以后的版本中得到了修复。
变通的尝试(玩具例子,成功)
为了使结构化绑定与我的编译器版本一起工作,我尝试通过专门化的std::tuple_size
、std::tuple_element
和std::get
创建一个解决方案。有关详细信息,请参阅这个cppreference首选项链接。
为了简单起见,我首先成功地尝试了这个玩具结构。以下是专门化,查看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
请注意,如果删除#ifdef
for g++ 7.1.,编译将失败,错误与上面(...is not an integral constant expression
)相同。(有趣的是:与boost::bimap
示例不同,该示例只在g++ 7.4上编译得很好,玩具示例已经成功地使用了g++ 7.2)
解决办法(最初的例子,不成功)
现在,我非常确信我找到了解决方案,所以我尝试着对boost::bimap
做同样的事情,但是我无能为力地失败了(在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; };
}
错误消息太长,无法在这里发布(请参阅戈德波特输出),但基本上我理解编译器没有匹配“我的”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);
}
我做错了什么吗?还是这个错误给我的解决方案造成了不可逾越的障碍?
发布于 2019-05-07 17:11:03
我不认为这是你能解决的问题。
这里有一个较短的复制:
#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的措辞强调了我的意思:
不合格的id get是通过类成员访问查找(basic.lookup.classref)在
E
范围内查找的,如果找到至少一个声明是函数模板,其第一个模板参数是非类型参数,则初始化器是e.get<i>()
。否则,初始化器是get<i>(e)
,其中get在关联的命名空间(basic.lookup.argdep)中查找。
N::X
有一个接受类型模板参数的成员函数模板get()
,这一事实应该会导致我们考虑在get
上查找ADL,这应该会找到非会员的N::get
。gcc 7.4这样做是正确的,gcc 7.3抱怨N::X::get()
没有工作。
解决这个问题的唯一方法是以某种方式包装初始化器。基本上是这样做的:
auto [i] = wrap(N::X{});
其中,wrap
返回一些肯定没有名为get
的成员的新类型,这样您就可以提供所需的非成员。我不确定这里是否有不需要额外包装的解决方案。除了使用gcc 7.4 :-)
https://stackoverflow.com/questions/56026067
复制相似问题