c++ - MSVC 可以找到匹配的构造函数,但 gcc 和 clang 找不到
问题描述
所以最近我对制作一个带标签的元组很感兴趣,它是一个元组,但你可以使用带有标签而不是索引的 get 函数。代码对我来说有点长。
// tagged_tuple.hpp
// simple class to hold two types
// works with incomplete types, important for our use case
template <class First, class Second> struct type_pair {
using first = First;
using second = Second;
};
namespace detail {
// gets info about a classes name tag (key -> value type mapping) from the class
template <class T>
struct name_tag_traits {
// aliases for our specific use case
using tag_type = typename T::first;
using value_type = typename T::second;
};
// helper alias, turns a list of TypePairs supplied to tagged_tuple to a list of
// key/tag/name types
template <class T>
using name_tag_t = typename name_tag_traits<T>::tag_type;
// same as above but returns a list of value types
template <class T>
using name_tag_value_t = typename name_tag_traits<T>::value_type;
template <class T> struct Base {};
template <class... Ts>
struct TypeSet : Base<Ts>... {
template <class T>
constexpr auto operator + (Base<T>) {
if constexpr (std::is_base_of_v<Base<T>, TypeSet>) {
return TypeSet{};
}
else {
return TypeSet<Ts..., T>{};
}
}
constexpr auto size() const -> std::size_t { return sizeof...(Ts); }
};
// checks if tags are unique
template <class... Types>
constexpr auto are_tags_unique() -> bool {
constexpr auto typeset = (TypeSet<>{} + ... + Base<name_tag_t<Types>>{});
return typeset.size() == sizeof...(Types);
}
template <class Needle>
constexpr auto index_of_impl(size_t index, size_t end) -> std::size_t {
return end;
};
template <class Needle, class T, class... Haystack>
constexpr auto index_of_impl(size_t index, size_t end) -> std::size_t {
return std::is_same<Needle, T>::value
? index
: index_of_impl<Needle, Haystack...>(index + 1, end);
};
// find the index of T in a type list, returns sizeof...(Haystack) + 1 on failure (think std::end())
template <class Needle, class... Haystack>
static constexpr auto index_of() -> std::size_t {
return index_of_impl<Needle, Haystack...>(0, sizeof...(Haystack) + 1);
};
}; // namespace detail
// and here's our little wrapper class that enables tagged tuples
template <class... TypePairs>
class tagged_tuple : public std::tuple<detail::name_tag_value_t<TypePairs>...> {
public:
// throws an error if tags are not unique
static_assert(detail::are_tags_unique<TypePairs...>(), "Duplicated tags!");
// not really needed for now but if we switch to private inheritance it'll come in handy
using tag_type = std::tuple<detail::name_tag_t<TypePairs>...>;
using value_type = std::tuple<detail::name_tag_value_t<TypePairs>...>;
using value_type::value_type;
using value_type::swap;
using value_type::operator =;
};
// our special get functions
template <class Name, class... TypePairs>
auto get(tagged_tuple<TypePairs...>& tuple) -> typename std::tuple_element<
detail::index_of<Name, detail::name_tag_t<TypePairs>...>(),
typename tagged_tuple<TypePairs...>::value_type>::type& {
return std::get<detail::index_of<Name, detail::name_tag_t<TypePairs>...>()>(
tuple);
};
template <class Name, class... TypePairs>
auto get(const tagged_tuple<TypePairs...>& tuple) -> const typename std::tuple_element<
detail::index_of<Name, detail::name_tag_t<TypePairs>...>(),
typename tagged_tuple<TypePairs...>::value_type>::type& {
return std::get<detail::index_of<Name, detail::name_tag_t<TypePairs>...>()>(
tuple);
};
template <class Name, class... TypePairs>
auto get(tagged_tuple<TypePairs...>&& tuple) -> typename std::tuple_element<
detail::index_of<Name, detail::name_tag_t<TypePairs>...>(),
typename tagged_tuple<TypePairs...>::value_type>::type&& {
return std::get<detail::index_of<Name, detail::name_tag_t<TypePairs>...>()>(
std::move(tuple));
};
语法是:
// main.cpp
using foo = tagged_tuple<type_pair<class bar, int>, type_pair<class baz, std::string>>;
foo a{ 1, "foo" };
std::cout << get<bar>(a) << " " << get<baz>(a);
输出:
1 foo
一切都很顺利,直到我测试了这段代码:
std::tuple<int, std::string> a{ 1, "foo" };
foo b{ a };
在我的理论中应该可以工作,因为 tagged_tuple 是从 std::tuple 派生的,并且它有一个针对这种情况的构造函数。然后我在 Visual Studio 2019 中进行了测试,它完全有效。但是当我尝试使用 gcc/clang 时,我得到了这个错误:
error: no matching constructor for initialization of 'foo' (aka 'tagged_tuple<type_pair<class bar, int>, type_pair<class baz, std::string>>')
foo b{ a };
这很奇怪,因为我已经包含了所有基类的构造函数using value_type::value_type
(据我所知)。然后我添加了以下构造函数:
template <class... Types>
constexpr tagged_tuple(std::tuple<Types...> &tuple) : value_type(tuple) {}
而这一次它奏效了。我不明白为什么没有这段代码它就不能工作。
gcc/clang 中是否有错误或我遗漏了什么?
解决方案
推荐阅读
- r - 祖先状态重建,多分法和没有分支长度
- django - django 按非相关表中的汇总值排序
- vagrant - Vagrant up Vm问题(MAC)
- javascript - 尝试禁用并稍后启用 javascript 中的按钮
- java - Java Thread 启动和运行函数
- excel - Excel公式转换成VBA
- android - 为什么camera2录制的视频不好?
- unity3d - 如何在 Unity 中将 Asset Bundle 上传到远程服务器
- spring-boot - 在 spring 调用 save 方法后,有没有办法更新并将名称发送到响应?
- mysql - 如何将 MySQL 中每个状态的结果限制为 10 个客户