从初始化列表初始化std::tuple

63
我在想元组是否可以通过初始化列表来进行初始化(更准确地说,通过初始化列表的初始化列表)?考虑到元组的定义:
typedef std::tuple< std::array<short, 3>,
                    std::array<float, 2>,
                    std::array<unsigned char, 4>,
                    std::array<unsigned char, 4> > vertex;

有没有办法做到以下的事情:
static vertex const nullvertex = { {{0, 0, 0}},
                                   {{0.0, 0.0}},
                                   {{0, 0, 0, 0}},
                                   {{0, 0, 0, 0}} };

我只是想通过使用结构体而不是元组来实现相同的功能(因此只有数组通过初始化列表进行初始化)。
static struct vertex {
    std::array<short, 3> m_vertex_coords;
    std::array<float, 2> m_texture_coords;
    std::array<unsigned char, 4> m_color_1;
    std::array<unsigned char, 4> m_color_2;
} const nullvertex = {
    {{0, 0, 0}},
    {{0.0, 0.0}},
    {{0, 0, 0, 0}},
    {{0, 0, 0, 0}}
};

没有理由我必须使用元组,只是好奇而已。我问这个问题是因为我无法解决由我尝试初始化元组而产生的g++模板错误。
@Motti:所以我错过了统一初始化的正确语法。
static vertex const nullvertex = vertex{ {{0, 0, 0}},
                                         {{0.0, 0.0}},
                                         {{0, 0, 0, 0}},
                                         {{0, 0, 0, 0}} };

static vertex const nullvertex{ {{0, 0, 0}},
                                {{0.0, 0.0}},
                                {{0, 0, 0, 0}},
                                {{0, 0, 0, 0}} };

但似乎所有的麻烦都在于数组,它们没有初始化列表的构造函数,而且用适当的构造函数包装数组似乎并不是一件容易的任务。
1个回答

72

对于元组,初始化列表并不相关。

我认为你混淆了C++0x中花括号的两种不同用法。

  1. initializer_list<T>是一个同类型集合(所有成员必须是相同类型,因此对于std::tuple不相关)。
  2. Uniform initialization是使用花括号构造各种对象的方法;数组、POD和具有构造函数的类。这也有助于解决the most vexing parse的问题。

以下是简化版本:

std::tuple<int, char> t = { 1, '1' }; 
// error: converting to 'std::tuple<int, char>' from initializer list would use
// explicit constructor 'std::tuple<_T1, _T2>::tuple(_U1&&, _U2&&) 
// [with _U1 = int, _U2 = char, _T1 = int, _T2 = char]'

std::tuple<int, char> t { 1, '1' }; // note no assignment
// OK, but not an initializer list, uniform initialization

错误信息表明您尝试隐式调用构造函数,但它是一个显式构造函数,因此您不能这样做。
基本上,您正在尝试做的事情类似于这样:
struct A { 
    explicit A(int) {}
};

A a0 = 3;
// Error: conversion from 'int' to non-scalar type 'A' requested

A a1 = {3}; 
// Error: converting to 'const A' from initializer list would use 
// explicit constructor 'A::A(int)'

A a2(3); // OK C++98 style
A a3{3}; // OK C++0x Uniform initialization

13
使用花括号初始化列表构造std::tuple为什么是不好的事情?这种方法对于std::pair有效,并且std::tuplestd::pair的推广,因此我不理解这种限制的原因:S... - rubenvb
6
使用统一初始化方式(大括号)初始化一个元组是可以的,但必须去掉等号。如果使用了等号,则意味着你正在使用一个参数构造函数接受初始化列表来构造临时对象,然后使用临时值的复制构造函数(尽管编译器可能会省略其中的一些步骤)。 - Motti
5
那是对@rubenvb的回复非常不好的评论,不会引入任何临时变量。相应的构造函数为显式构造函数仅仅是一种耻辱。 - Johannes Schaub - litb
1
@JohannesSchaub-litb,如果我没记错的话,当你写T t = x;时,它要求T有一个可访问的隐式构造函数,接受x并具有可访问的复制构造函数。一旦确定t可以从临时T构造,编译器就可以省略临时变量,否则它不应该编译。 - Motti
我认为简化版本 std::tuple<int, char> t = { 1, '1' }; 不好,因为 std::tuple<int, char> t = { (int)1, (char)'1' }; 仍然会生成相同的错误。正如这里所解释的那样,错误消息是在此处生成的,因为元组没有接受初始化列表作为tuple constructor的构造函数。 - writalnaie

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