求助,朋友遇到的一道 C++ 面试题

2020-04-06 06:35:22 +08:00
 CismonX

题目

如下是一个简单封装了 std::tuplemy_tuple 类模板实现,请根据以下要求对其进行扩充:(仅允许使用 STL,不允许使用 Boost.MPL 等外部依赖)

template <typename... Ts>
struct my_tuple {

    using T = std::tuple<Ts...>;

    T instance;

    template <std::size_t I>
    std::tuple_element_t<I, T>& get() {
        return std::get<I>(instance);
    }
};

(1):支持在编译期向一个 my_tuple<...> 实例中添加更多的类型,语法和预期效果如下所示:

int main() {
    auto tuple = my_tuple<int>::append<float, char, long>
                              ::append<bool, double>
                              ::append<std::vector<int>>();
    std::vector<int>& vector = tuple.get<6>();
}

(2):在 (1) 的基础上,对 my_tuple 的模板参数进行限制,不允许重复的类型存在,否则在实例化 my_tuple<...> 时抛出编译期错误:

int main() {
    auto tuple = my_tuple<int, int>();                      // Error!
    auto tuple1 = my_tuple<int, double>::append<double>();  // Error!
}

(3):在 (2) 的基础上,允许根据类型而不是下标,从 my_tuple<...> 实例中获取对应的元素:

int main() {
    auto tuple = my_tuple<int>::append<std::vector<int>>();
    std::vector<int>& vector = tuple.get<std::vector<int>>();
}

求助

朋友当时遇到这道题,只写出来第一问,从第二问开始一直通不过编译,面试官看他做不出来就跳过了。

后来我拿到这道题看了下,感觉挺惊讶的,以前从来没见过 metaprogramming 的面试题。试着做了一下,第一问确实很简单,只要记得 std::tuple_cat 基本上就没问题了,我的解答如下:

template <typename... Ts>
struct my_tuple;

template <typename>
struct unpack_tuple;

template <typename... Ts>
struct unpack_tuple<std::tuple<Ts...>> {
    using type = my_tuple<Ts...>;
};

template <typename... Ts>
using unpack_tuple_t = typename unpack_tuple<Ts...>::type;

template <typename... Ts>
struct my_tuple {
    using T = std::tuple<Ts...>;

    template <typename... Ks>
    using append = unpack_tuple_t<decltype(
        std::tuple_cat(std::declval<T>(), std::declval<std::tuple<Ks...>>()))>;

    T instance;

    template <std::size_t I>
    std::tuple_element_t<I, T>& get() {
        return std::get<I>(instance);
    }
};

但是从第二问开始就没有头绪了,隐约感觉应该可以用到 std::index_sequencestd::disjunction,但不知道该如何下手。还请 V 站各位大佬指教。

1579 次点击
所在节点    问与答
4 条回复
liuy1994g
2020-04-06 06:41:52 +08:00
这也太早了吧
geelaw
2020-04-06 06:45:45 +08:00
你不需要知道 tuple_cat 或者 index_sequence 或者 disjunction,这些全都可以通过基础手段实现。

第二个的思路:列表 A 没有重复等价于 (A 是空的) 或 (A 第一个元素不等于其他且 A 去掉第一个后无重复)。

第三个的思路:可以通过枚举算出一个元素在列表里的位置。
owwlo
2020-04-06 10:34:09 +08:00
稍微修改了一下你的原答案,不好意思可能写得有点乱,有些地方很冗余。在 https://wandbox.org/上简单的测试过~ 出错的地方还请大家指教 ;)

```c++
#include <vector>
#include <tuple>

using namespace std;

template <typename... Ts>
struct my_tuple;

template<typename A, typename B>
struct tuple_contains;

template <typename checkT>
struct tuple_contains<std::tuple<>, checkT>
{
constexpr static bool value = false;
};

template <typename headT, typename checkT>
struct tuple_contains<std::tuple<headT>, checkT>
{
constexpr static bool value = std::is_same<headT, checkT>::value;
};

template <typename headT, typename... tailT, typename checkT>
struct tuple_contains<std::tuple<headT, tailT...>, checkT>
{
constexpr static bool value = std::is_same<headT, checkT>::value || tuple_contains<std::tuple<tailT...>, checkT>::value;
};

template<typename A, typename B>
struct tuple_count;

template <typename checkT>
struct tuple_count<std::tuple<>, checkT>
{
constexpr static size_t value = 0;
};

template <typename headT, typename checkT>
struct tuple_count<std::tuple<headT>, checkT>
{
constexpr static size_t value = std::is_same<headT, checkT>::value ? 1 : 0;
};

template <typename headT, typename... tailT, typename checkT>
struct tuple_count<std::tuple<headT, tailT...>, checkT>
{
constexpr static size_t value = (std::is_same<headT, checkT>::value ? 1 : 0 ) + tuple_count<std::tuple<tailT...>, checkT>::value;
};

template<typename A>
struct check_tuple_unique;

template<>
struct check_tuple_unique<std::tuple<>>
{
constexpr static bool value = true;
};

template <typename headT>
struct check_tuple_unique<std::tuple<headT>>
{
constexpr static bool value = true;
};

template <typename headT, typename... tailT>
struct check_tuple_unique<std::tuple<headT, tailT...>>
{
constexpr static bool value = (tuple_count<std::tuple<tailT...>, headT>::value == 0) && check_tuple_unique<std::tuple<tailT...>>::value == true;
};

template<typename A, typename B, typename C>
struct index_of_details;

template <typename... leftT, typename headT, typename checkT>
struct index_of_details<std::tuple<leftT...>, std::tuple<headT>, checkT>
{
constexpr static size_t value = std::is_same<headT, checkT>::value ? tuple_size<std::tuple<leftT...>>::value : -1;
};

template <typename... leftT, typename headT, typename... tailT, typename checkT>
struct index_of_details<std::tuple<leftT...>, std::tuple<headT, tailT...>, checkT>
{
constexpr static size_t value = std::is_same<headT, checkT>::value ? tuple_size<std::tuple<leftT...>>::value : index_of_details<std::tuple<leftT..., headT>, std::tuple<tailT...>, checkT>::value;
};

template<typename A, typename B>
struct index_of;

template <typename checkT, typename... tailT>
struct index_of<std::tuple<tailT...>, checkT>
{
constexpr static size_t value = index_of_details<std::tuple<>, std::tuple<tailT...>, checkT>::value;
};

template <typename... Ts>
struct my_tuple {
using T = std::tuple<Ts...>;

template <typename... Ks>
using append = my_tuple<Ts..., Ks...>;

my_tuple()
{
static_assert(check_tuple_unique<T>::value, "Wakaka! duplicated found!");
}

T instance;

template <std::size_t I>
std::tuple_element_t<I, T>& get() {
return std::get<I>(instance);
}

template<typename targetT>
auto& get() {
return std::get<index_of<T, targetT>::value>(instance);
}

};


int main()
{
static_assert(tuple_contains<std::tuple<>, int>::value == false, "wrong answer");
static_assert(tuple_contains<std::tuple<int>, int>::value == true, "wrong answer");
static_assert(tuple_contains<std::tuple<double, double>, int>::value == false, "wrong answer");

static_assert(tuple_count<std::tuple<>, int>::value == 0, "wrong answer");
static_assert(tuple_count<std::tuple<int>, int>::value == 1, "wrong answer");
static_assert(tuple_count<std::tuple<int, double, int>, int>::value == 2, "wrong answer");

static_assert(check_tuple_unique<std::tuple<>>::value == true, "wrong answer");
static_assert(check_tuple_unique<std::tuple<int>>::value == true, "wrong answer");
static_assert(check_tuple_unique<std::tuple<int, double, int>>::value == false, "wrong answer");
static_assert(check_tuple_unique<std::tuple<int, int>>::value == false, "wrong answer");
static_assert(check_tuple_unique<std::tuple<int, double, std::vector<int>>>::value == true, "wrong answer");

// this will fail
// auto tuple = my_tuple<int>::append<float, char, long>
// ::append<bool, double>
// ::append<std::vector<int>, int>();
auto tuple = my_tuple<int>::append<float, char, long>
::append<bool, double>
::append<std::vector<int>>();
std::vector<int>& vector = tuple.get<6>();
std::vector<int>& vector2 = tuple.get<std::vector<int>>();

auto tuple2 = my_tuple<int>::append<std::vector<int>>();
std::vector<int>& vector3 = tuple.get<std::vector<int>>();
int& vector4 = tuple.get<int>();
}
```
owwlo
2020-04-06 10:42:35 +08:00
T T 咋在回复里高亮 c++代码啊?

这是一个专为移动设备优化的页面(即为了让你能够在 Google 搜索结果里秒开这个页面),如果你希望参与 V2EX 社区的讨论,你可以继续到 V2EX 上打开本讨论主题的完整版本。

https://www.v2ex.com/t/659735

V2EX 是创意工作者们的社区,是一个分享自己正在做的有趣事物、交流想法,可以遇见新朋友甚至新机会的地方。

V2EX is a community of developers, designers and creative people.

© 2021 V2EX