C++是否有一个标准的编译时类型容器?

6

(这个问题的答案由Nim提供,其中提到了boost::mpl::map。)

标准C++中是否有一个编译时容器可以保存类型?

使用示例:

compiler::vector foo{char, short, long, long long};

template <int N>
void bar(foo[N] param){/*do something and return foo[N]*/}

1
MPL将其表示为boost::mpl::vector - Joseph Mansfield
没有上下文,我不确定这是否对您有帮助,但我认为可变参数模板参数可以是解决问题的一种方式,因为模板参数序列可以被视为类型容器。 - Bérenger
1
这并不是一个真正的答案,但是当涉及到类型列表时,我通常会前往Loki(http://loki-lib.sourceforge.net/html/a00681.html)。Boost已经被提到过了,但在你不能(或不想)使用boost的情况下,Loki有很多解决方案。 - kwierman
@Bérenger 所以我猜你的可变参数模板想法会类似于 Dan 对这个问题的回答 - Jonathan Mee
1
我认为do something and return foo[T]是相关的,因为有一个隐含的编译时代码层面无法工作的问题。也就是说,一个函数不能返回多个类型。然而,你可以进行特化或委托给重载函数,这样就能正常工作了。 - Mooing Duck
显示剩余3条评论
3个回答

3

在C++11中,您可以使用std::tuple:(免责声明:未经测试)

#include <tuple>
#include <type_traits>
std::tuple<char, short, long, long long> foo;

// reference type
template <int N>
void bar(decltype(std::get<N>(foo)) param){...}


// value type
template <int N>
void bar(std::remove_reference<decltype(std::get<N>(foo))>::type param)

请注意,这并不完全符合您的要求,因为您将只有值或引用类型,即使两者都混合在foo的元组声明中。
元组的值从未被使用。我认为通过编译器优化,foo 实际上永远不会在对象代码中实例化。

2
你甚至不需要 foo,只需要 decltypedeclval - Lightness Races in Orbit
2
我认为 decltype(std::get<T>(foo)) 会在索引 T 处将一个意外的引用添加到类型中,因为 std::get 将返回一个引用类型。 - Julian
1
@LightnessRacesinOrbit:如果你检查std::get的返回类型,例如这里,你会发现它永远不会返回纯类型,而总是返回一个rvalue或lvalue引用。至少对我来说,以下断言没有错误:static_assert(std::is_same<int&&,decltype(std::get<0>(std::declval<std::tuple<int>>()))>::value,"error") - Julian
@Bérenger 看起来测试很好,但请原谅这个含糊不清的例子:http://ideone.com/e.js/EXsM9o。另外,如果你有C++14的话,我建议你使用`remover_reference_t`。 - Jonathan Mee
@Julian:是的,我知道std::get的作用,但正如我所说,你忘记了decltype的作用。请查看我提供的链接。将rvalue表达式命名为lvalue引用时,引用类型会被删除。你使用declval的示例与之前的示例不同。 - Lightness Races in Orbit
显示剩余8条评论

3
作为一种类型容器,标准库提供了 std::tuple。正如 bogdan 所评论的那样,您可以使用 std::tuple_element 访问类型元素。
using foo = std::tuple<char, short&, const long&&, long long>;

template <int N>
typename std::tuple_element<N,foo>::type bar(){/*...*/}

即使没有 std::tuple_element,你也可以轻松构建自己的元组元素类型:
/// We want a metafunction to accept an index N into our type list LIST
template <unsigned N, typename LIST> struct
tuple_element;

/// Specialization for the case where N==0
template <template <typename ...> class LIST_T,typename T,typename...ELMS> struct
tuple_element<0,LIST_T<T,ELMS...>> {
    using type = T; // just take the first type from the list
};

template <unsigned N, template <typename ...> class LIST_T,typename T,typename...ELMS> struct
tuple_element<N,LIST_T<T,ELMS...>> {
    /// fallback for N>0: delegate the result type recursively until N->0
    using type = typename tuple_element<N-1,LIST_T<ELMS...>>::type;
};

// create a convenience wrapper for the template
template <unsigned N, typename LIST> using
type_at = typename tuple_element<N, LIST>::type;

现在你可以定义你的类型列表,例如像这样:
using foo = std::tuple<char, short&, const long&&, long long>;

您可以使用type_at<N, foo>轻松访问其元素:

static_assert(std::is_same< type_at<0,foo>, char>::value,"error");
static_assert(std::is_same< type_at<1,foo>, short&>::value,"error");
static_assert(std::is_same< type_at<2,foo>, const long&&>::value,"error");
static_assert(std::is_same< type_at<3,foo>, long long>::value,"error");

template <int N>
type_at<N,foo> bar(){/*...*/}

1
std::tuple_element 不符合“类型访问函数”的资格吗? - bogdan
@bogdan:不错,我之前不知道这个。我会相应地调整答案。 - Julian

0

这篇文章基于Bérenger的回答,他让我了解了tuple的概念。但我相信我们可以做得更好,甚至保留引用:

tuple foo<char&, short&, long&, long long&>;

template <int N>
void bar(tuple_element_t<N, decltype(foo)> param){}

事实上,如果在这个函数之外没有使用 `foo` 的计划,我们甚至可以内联声明它:
template <int N>
void bar(tuple_element_t<N, tuple<char&, short&, long&, long long&>> param){}

@Downvoter,您能否对投票下降的原因进行一些澄清?对我来说,这绝对是一个很好的解决方案。 - Jonathan Mee

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