C++:std::tuple_size/tuple_element可以进行特化吗?

22
< p > 对于自定义类型,是否允许使用 std::tuple_sizestd::tuple_element 进行特化?我想是可以的,但我想要确定,并且我找不到任何具体信息。 < /p > < p > 例如(省略命名空间、成员函数和 get<I> 重载):< /p >
template <typename T, size_t N>
struct vector { T _data[N]; };

template<size_t I, typename T, size_t N>
constexpr T& get(vector<T,N>& vec) { return vec._data[I]; }

namespace std {
template<typename T, size_t N>
class tuple_size< vector<T,N> > : public std::integral_constant<size_t, N> { };
template<size_t I, typename T, size_t N>
class tuple_element< I, vector<T,N> > { public: using type = T; };
}

我需要它来与结构化绑定一起使用:

void f(vector<T,3> const& vec)
{
    auto& [x,y,z] = vec;
    // stuff...
}
1个回答

21

用户自定义类型的模板特化通常是可以的,并且一直都是如此。N4606,[namespace.std]/1:

程序可仅在声明依赖于用户自定义类型并且特化符合标准库原始模板的要求且未被明确禁止时,为任何标准库模板添加到命名空间std中。

对于tuple_size,原始模板的要求在[tuple.helper]/1中指定:

tuple_size<T> 的所有特化必须满足带有BaseCharacteristicintegral_constant<size_t, N>UnaryTypeTrait要求,其中N为某个值。

UnaryTypeTrait则在[meta.rqmts]/1中规定:

UnaryTypeTrait描述了类型的属性。它应该是一个类模板,接受一个模板类型参数和可能有助于定义所描述属性的其他参数。它应该是DefaultConstructibleCopyConstructible,并且直接或间接地从其BaseCharacteristic派生,并公开且明确地继承自integral_constant模板的特化,其中integral_constant的参数由描述特定属性的要求确定。 BaseCharacteristic的成员名称不应被隐藏,并且在UnaryTypeTrait中应明确可用。

tuple_element的要求在[tuple.helper]/6和[meta.rqmts]/3中指定,但出于简洁起见,我不会在这里发布它们。总之,确实可以进行特化...


1
请注意,在C++1z中,这样做是必需的,以使用新的auto[a,b,c]结构化绑定功能。 - Yakk - Adam Nevraumont
有没有办法用基类或公共接口来定义它们?我有很多 F<X...> ~= tuple<F<X>...>G<S, Y...> ~= tuple<G<S, Y>...> 等等,如果它们是元组的子类,我仍然必须定义 tuple_element<I, F<X...>>: tuple_element<I, tuple<F<X>...>> 等等。我能想到的就是打开通用的 tuple_size<S>/tuple_element<I,S>,使用 size() 或基类,但这似乎会引发麻烦。如果我做不到更好,我将不得不使用宏。 - John P
1
@John:有趣的是,我昨天刚好遇到了同样的问题。:-]不幸的是,我没有想出任何不需要对特质进行轻微侵入性更改的解决方案,但我掌控着相关的特质,所以我没有太费心思考。如果最终有一个聪明的非侵入式解决方案,我会很感兴趣! - ildjarn
1
@John:这是一个想法,使用C++20: https://godbolt.org/z/5zrKE9。我无法想到一种非侵入式的方法来使其工作(例如用于结构化绑定),除非使用概念,因此C++17代码可能没有机会。 - ildjarn
非常感谢!C++20看起来很疯狂。我的一次尝试实际上是template<class T> struct tuple_size<enable_if_t<is_tuple<typename T::type>::value, T>>: ...但遗憾的是T无法推导。我想这就是概念的用途。简单问题,那个is_convertible<D*,B*>技术是用来避免使用is_base_of<B,D>产生假负面吗?为什么要同时使用两者? - John P
@John: is_base_of 对于私有和模糊继承的基类返回 true;由于我们想要委托给 std::tupletuple_sizetuple_element 而不需要转换,我们需要确保我们的对象也可以隐式转换为 tuple。在这种情况下,is_base_of 检查实际上比我们严格需要的检查更强,因为我们可以只检查可转换性以支持具有元组成员和 operator std::tuple<...>& 而不是元组基类的类型,但我希望这个概念尽可能地描述 my_tuple_base,而不是实际命名它。 - ildjarn

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接