C++警告:将_U1推断为std::initializer_list<int>。

4

我正在使用g++-4.6.1 --std=c++0x编译一段代码时出现了一个警告,但我无法理解它的含义,请帮忙看一下:

#include <vector>
#include <tuple>

int main()
{
    std::tuple<std::vector<int>, int> t{ {1, 2, 3}, 10};
}

我收到了以下警告:
scratch.cpp: In function ‘int main()’:
scratch.cpp:6:55: warning: deducing ‘_U1’ as ‘std::initializer_list<int>’ [enabled by default]
/usr/local/include/c++/4.6.1/tuple:329:9: warning:   in call to ‘std::tuple<_T1, _T2>::tuple(_U1&&, _U2&&) [with _U1 = std::initializer_list<int>, _U2 = int, _T1 = std::vector<int>, _T2 = int][enabled by default]
scratch.cpp:6:55: warning:   (you can disable this with -fno-deduce-init-list) [enabled by default]

看这个实现:

template<typename _T1, typename _T2>
class tuple<_T1, _T2> : public _Tuple_impl<0, _T1, _T2>
    {
      typedef _Tuple_impl<0, _T1, _T2> _Inherited;

    public:
      //...

      template<typename _U1, typename _U2>
        explicit
        tuple(_U1&& __a1, _U2&& __a2)
        : _Inherited(std::forward<_U1>(__a1), std::forward<_U2>(__a2)) { }
    //...
}

似乎这个警告是在抱怨它从initializer_list<int>(代码中的{1, 2, 3})转发到std::vector<int>,后者将成为std::tuple中的第一个类型。 对我来说,这似乎完全没问题。
所以我的问题是:这个警告是什么意思?

我要注意的是,即使你打开所有警告和最大严格性,gcc 4.7(当前的开发主干)也不会对这段代码发出警告。鉴于C++0x在4.7中得到了大幅改进(无论是库还是编译器),很可能这是4.6的C++0x支持中的一个错误,或者标准在这个领域有了晚期变化。 - snogglethorpe
2个回答

5

我认为这更像是一种猜测而不是标准中的引用,但我认为它有些道理。

问题在于花括号包围的列表可能意味着很多东西。它可以是初始化列表,但根据统一初始化,它也可以是许多其他东西,例如聚合初始化程序或仅仅是构造函数参数。

考虑以下情况:

struct Bar { Bar(int, int, int){} };

void foo(const std::vector<int> & v);
void zoo(const Bar & v);

我可以调用foo({1,2,3})zoo({1,2,3})两个函数。因为这两个函数的参数类型是已知的,所以没有歧义。但是foo的参数被推断为初始化列表,而zoo的参数是一个统一初始化的构造函数调用。
在元组情况中,问题在于所有涉及的类型都是模板。构造函数的参数类型必须被推导出来;在函数调用时它是不已知的。它的真正作用只有在函数中才会显现出来。
由于可能有很多种方法来构造元组成员,这种推导可能是有歧义或者错误的,因此编译器会警告您它正在做出一个假设
事实上,您可以非常简单地构造这个假设的失败:
std::tuple<Bar, std::vector<int>> s{{1,2,3}, {1,2,3}};

这段代码会失败,因为花括号列表被错误地推断为第一个元组成员。编写元组的唯一“好”方法是显式地编写:

std::tuple<Bar, std::vector<int>> s{Bar{1,2,3}, std::vector<int>{1,2,3}};

3

GCC发出警告,因为推断初始化列表是一种扩展。我认为这个警告是为了符合标准,因为它应该被视为实现的诊断。


所以只用花括号作为参数实际上不符合标准? - Kerrek SB
@KerrekSB,我其实不知道确切的答案是什么。std::tuple<T...>有一个非模板构造函数,它接受(T const&...),所以为了回答你的问题,我们必须考虑为什么这个构造函数不应该被选择,而是选择模板构造函数,在那里进行推导。 - Luc Danton

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