2016-08-12 55 views
3
using Name = std::string; 
using NodeType = int; 
using ElementType = int; 

std::tuple<Name, NodeType, ElementType> myTuple("hi", 12, 42); 

cout<<std::get<NodeType>(myTuple)<<endl; 

这会产生编译错误。是否有可能通过重复的std :: tuple类型名称获取?

我可以使用std::get<1>(myTuple),但它不容易阅读为std::get<NodeType>(myTuple),有没有什么办法在这种情况下按类型获取价值?

+5

好像你可能只是一个命名的类最好避免头痛。 – TartanLlama

+0

@TartanLlama是的。唯一的问题是这个元组在其他文件中定义,我无法更改... – Deqing

+0

为什么你想要那个? – edmz

回答

4
enum {NodeTypeIdX=1, ElementTypeIdx=2}; 

std::get<NodeTypeIdx>(myTuple); 

这个作品是自我记录。

std::get<Type>语法不支持std::get<Type, which_one>或类似的东西。它只适用于类型是唯一的。由于您无法控制元组的定义,因此您可以执行任何操作(定义)以使std::get<Type>选项无效。

另一方面,谁在乎你使用的关键字是一个类型还是一个枚举常量?重要的部分是它有一个自我记录的名字。

+0

我使用了一个const int,但我相信enum是一样的。 – Deqing

7

NodeTypeElementType是相同的类型,即inttypedefusing不要创建一个新的类型,只有名字。

因此,不能按照类型获取最后一个条目,因为它们的类型在元组中不唯一。

+0

我们已经提出了多年来强烈/不透明typedefs的建议,并且他们中没有一个将其纳入标准。这个答案是我们确实需要它们标准化的另一个证明 – KABoissonneault

+1

下面是强类型定义的“解决方法”:enum class NodeType:int {};和enum class ElementType:int {};'。 – Xeo

0

假设NameNodeTypeElementType不同类型(标签或许可以帮助):

template <class T> 
struct Names; 

template <> struct Names<Name> { static constexpr int i = 0; }; 
template <> struct Names<NodeType> { static constexpr int i = 1; }; 
template <> struct Names<ElementType> { static constexpr int i = 2; }; 

你可以做空有点用辅助功能:

template <class T> 
constexpr auto index() -> int 
{ 
    return Names<T>::index; 
} 

所以,你得到:

cout<< std::get< index<NodeType>() >(myTuple) << endl; 

我会给你它不是最好的。

+0

如果类型是唯一的,那么可以直接执行OP直接从C++ 14开始的操作。 –

1

无论您如何命名它,类型都是相同的 - int,ElementTypeNodeType是等同的,无法区分它们。

我会写存取功能,并使用这些:

using Something = std::tuple<Name, NodeType, ElementType>; 

NodeType& nodeType(Something& tp) 
{ 
    return std::get<1>(tp); 
} 

NodeType nodeType(const Something& tp) 
{ 
    return std::get<1>(tp); 
} 

ElementType& elementType(Something& tp) 
{ 
    return std::get<2>(tp); 
} 

ElementType elementType(const Something& tp) 
{ 
    return std::get<2>(tp); 
} 
// ... 

cout << nodeType(myTuple) << endl; 
elementType(myTuple) = some_type; 
+0

为了模仿'std :: get',你应该返回引用(并且可能需要'tp'作为非const引用,或者提供超载)。或者可能使用带有转发参考的模板版本以避免代码重复;) – Holt

+0

@Holt是的,我应该。 (但是我太不熟悉转发引用来尝试那个引用。) – molbdnilo

+0

使用转发引用(例如, 'template auto nodeType_(Tuple && t){return std :: get <1>(std :: forward (t)); }',这会为你节省大量的写作(在任何情况下,你应该在'const'版本中返回'const'引用而不是值);) – Holt

0

在其他的答案mentionned,如果tuple包含一个以上的单intstd::get<int>(tuple)将无法​​正常工作,所以它不会为你工作。

但是,你可以让你自己的get<>()这是你想要的。以下代码定义了一个模板函数my_get<Et,P>(tuple),该函数将返回对tupleEt类型的(P+1)th元素的引用。

template<typename Et, size_t P, typename Ct, size_t N, typename Et2> 
typename std::enable_if<std::is_same<Et,Et2>::value && (P == 0), Et&>::type 
    my_get(Ct& c, Et2& e) { return std::get<N>(c); } 

template<typename Et, size_t P, typename Ct, size_t N, typename Et2> 
typename std::enable_if<std::is_same<Et,Et2>::value && (P > 0), Et&>::type 
    my_get(Ct& c, Et2& e) { return my_get<Et,P-1,Ct,N+1>(c); } 

template<typename Et, size_t P, typename Ct, size_t N, typename Et2> 
typename std::enable_if<!std::is_same<Et,Et2>::value, Et&>::type 
    my_get(Ct& c, Et2& e) { return my_get<Et, P, Ct, N+1>(c, std::get<N+1>(c)); } 

template<typename Et, size_t P, typename Ct, size_t N> 
Et& my_get(Ct& c) { return my_get<Et, P, Ct, N>(c, std::get<N>(c)); } 

template<typename Et, size_t P, typename Ct> 
Et& my_get(Ct& c) { return my_get<Et, P, Ct, 0>(c); } 

用法:

void tptest() 
{ 
    std::tuple<double,int,double,int> mytuple(1.0,2, 3.0, 4); 
    std::cout << "my_get<double,0>(mytuple) = " << my_get<double,0>(mytuple) << std::endl; 
    std::cout << "my_get<int,0>(mytuple) = " << my_get<int,0>(mytuple) << std::endl; 
    std::cout << "my_get<double,1>(mytuple) = " << my_get<double,1>(mytuple) << std::endl; 
    std::cout << "my_get<int,1>(mytuple) = " << my_get<int,1>(mytuple) << std::endl; 
} 

输出:

my_get<double,0>(mytuple) = 1 
my_get<int,0>(mytuple) = 2 
my_get<double,1>(mytuple) = 3 
my_get<int,1>(mytuple) = 4