const auto std::initializer_list Clang 和 GCC 的区别
const auto std::initializer_list difference between Clang and GCC
我试图了解在组合初始化列表和 const auto
时 C++11 的正确行为应该是什么。对于以下代码,我在 GCC 和 Clang 之间得到了不同的行为,我想知道哪个是正确的:
#include <iostream>
#include <typeinfo>
#include <vector>
int main()
{
const std::initializer_list<int> l1 = { 1, 2, 3 };
const auto l2 = { 1, 2, 3 };
std::cout << "explicit: " << typeid(l1).name() << std::endl;
std::cout << "auto: " << typeid(l2).name() << std::endl;
}
用 g++ 编译的输出是:
explicit: St16initializer_listIiE
auto: St16initializer_listIKiE
而 clang++ 编译版本产生:
explicit: St16initializer_listIiE
auto: St16initializer_listIiE
似乎 GCC 正在将 auto
行变成 std::initializer_list<const int>
而 Clang 生成 std::initializer_list<int>
。当我使用 GCC 版本初始化 std::vector
时,它会产生问题。所以下面的工作在 Clang 下,但为 GCC 产生编译器错误。
// Compiles under clang but fails for GCC because l4
std::vector<int> v2 { l2 };
如果 GCC 正在生成正确的版本,那么似乎建议应该扩展各种 STL 容器以包含针对这些情况的另一个列表初始化程序重载。
注意:此行为在 GCC(4.8、4.9、5.2)和 Clang(3.4 和 3.6)的多个版本中似乎是一致的。
有一个关于这个和类似案例的 gcc 错误报告 wrong auto deduction from braced-init-list,Richard Smith 指出这是一个 gcc 错误:
Even simpler testcase:
#include <initializer_list>
const auto r = { 1, 2, 3 };
using X = decltype(r);
using X = const std::initializer_list<int>;
fails because decltype(r)
is deduced as const std::initializer_list<const int>
rather than const std::initializer_list<int>
.
C++ 标准草案的部分是 7.1.6.4
[dcl.spec.auto] 部分,它说:
When a variable declared using a placeholder type is initialized, or a return statement occurs in a function
declared with a return type that contains a placeholder type, the deduced return type or variable type
is determined from the type of its initializer. [...] Let T be the declared type of the variable or return type of the function. If the
placeholder is the auto type-specifier, the deduced type is determined using the rules for template argument
deduction. [...] Otherwise, obtain P from T by replacing the occurrences of auto with either a
new invented type template parameter U or, if the initializer is a braced-init-list, with std::initializer_-
list. Deduce a value for U using the rules of template argument deduction from a function call (14.8.2.1),
where P is a function template parameter type and the initializer is the corresponding argument [...] [ Example:
auto x1 = { 1, 2 }; // decltype(x1) is std::initializer_list<int>
auto x2 = { 1, 2.0 }; // error: cannot deduce element type
—end example ] [ Example:
const auto &i = expr;
The type of i is the deduced type of the parameter u in the call f(expr) of the following invented function template:
template <class U> void f(const U& u);
—end example ]
海湾合作委员会错误。 [dcl.spec.auto]/p7(引用 N4527):
When a variable declared using a placeholder type is initialized,
[...] the deduced return type or variable type is determined from the
type of its initializer. [...] Otherwise, let T
be the declared type
of the variable [...]. If the placeholder is the auto
type-specifier, the deduced type is determined using the rules for template argument deduction. If the initialization is
direct-list-initialization [...]. [...] Otherwise, obtain P
from T
by replacing the occurrences of auto
with either a new invented
type template parameter U
or, if the initialization is
copy-list-initialization, with std::initializer_list<U>
. Deduce a value for U
using the rules of template argument deduction from a
function call (14.8.2.1), where P
is a function template parameter
type and the corresponding argument is the initializer [...]. If the
deduction fails, the declaration is ill-formed. Otherwise, the type
deduced for the variable or return type is obtained by substituting
the deduced U
into P
.
因此,在const auto l2 = { 1, 2, 3 };
中,推导如同函数模板
template<class U> void meow(const std::initializer_list<U>);
给出调用 meow({1, 2, 3})
。
现在考虑无 const 的情况 auto l3 = { 1, 2, 3 };
(GCC 正确推断为 std::initializer_list<int>
)。这种情况下的推导就像函数模板
一样执行
template<class U> void purr(std::initializer_list<U>);
给出调用 purr({1, 2, 3})
。
由于函数参数的顶级 cv 限定被忽略,很明显这两个推导应该产生相同的类型。
[temp.deduct.call]/p1:
Template argument deduction is done by comparing each function
template parameter type (call it P
) with the type of the
corresponding argument of the call (call it A
) as described below.
If P
is a dependent type, removing references and cv-qualifiers from
P
gives std::initializer_list<P'>
[...] for some P'
[...] and
the argument is a non-empty initializer list (8.5.4), then deduction
is performed instead for each element of the initializer list, taking
P'
as a function template parameter type and the initializer element
as its argument.
根据 1
、2
或 3
推导 P'
(即 U
),类型为 int
的所有文字,显然会产生 int
.
我试图了解在组合初始化列表和 const auto
时 C++11 的正确行为应该是什么。对于以下代码,我在 GCC 和 Clang 之间得到了不同的行为,我想知道哪个是正确的:
#include <iostream>
#include <typeinfo>
#include <vector>
int main()
{
const std::initializer_list<int> l1 = { 1, 2, 3 };
const auto l2 = { 1, 2, 3 };
std::cout << "explicit: " << typeid(l1).name() << std::endl;
std::cout << "auto: " << typeid(l2).name() << std::endl;
}
用 g++ 编译的输出是:
explicit: St16initializer_listIiE
auto: St16initializer_listIKiE
而 clang++ 编译版本产生:
explicit: St16initializer_listIiE
auto: St16initializer_listIiE
似乎 GCC 正在将 auto
行变成 std::initializer_list<const int>
而 Clang 生成 std::initializer_list<int>
。当我使用 GCC 版本初始化 std::vector
时,它会产生问题。所以下面的工作在 Clang 下,但为 GCC 产生编译器错误。
// Compiles under clang but fails for GCC because l4
std::vector<int> v2 { l2 };
如果 GCC 正在生成正确的版本,那么似乎建议应该扩展各种 STL 容器以包含针对这些情况的另一个列表初始化程序重载。
注意:此行为在 GCC(4.8、4.9、5.2)和 Clang(3.4 和 3.6)的多个版本中似乎是一致的。
有一个关于这个和类似案例的 gcc 错误报告 wrong auto deduction from braced-init-list,Richard Smith 指出这是一个 gcc 错误:
Even simpler testcase:
#include <initializer_list> const auto r = { 1, 2, 3 }; using X = decltype(r); using X = const std::initializer_list<int>;
fails because
decltype(r)
is deduced asconst std::initializer_list<const int>
rather thanconst std::initializer_list<int>
.
C++ 标准草案的部分是 7.1.6.4
[dcl.spec.auto] 部分,它说:
When a variable declared using a placeholder type is initialized, or a return statement occurs in a function declared with a return type that contains a placeholder type, the deduced return type or variable type is determined from the type of its initializer. [...] Let T be the declared type of the variable or return type of the function. If the placeholder is the auto type-specifier, the deduced type is determined using the rules for template argument deduction. [...] Otherwise, obtain P from T by replacing the occurrences of auto with either a new invented type template parameter U or, if the initializer is a braced-init-list, with std::initializer_- list. Deduce a value for U using the rules of template argument deduction from a function call (14.8.2.1), where P is a function template parameter type and the initializer is the corresponding argument [...] [ Example:
auto x1 = { 1, 2 }; // decltype(x1) is std::initializer_list<int> auto x2 = { 1, 2.0 }; // error: cannot deduce element type
—end example ] [ Example:
const auto &i = expr;
The type of i is the deduced type of the parameter u in the call f(expr) of the following invented function template:
template <class U> void f(const U& u);
—end example ]
海湾合作委员会错误。 [dcl.spec.auto]/p7(引用 N4527):
When a variable declared using a placeholder type is initialized, [...] the deduced return type or variable type is determined from the type of its initializer. [...] Otherwise, let
T
be the declared type of the variable [...]. If the placeholder is theauto
type-specifier, the deduced type is determined using the rules for template argument deduction. If the initialization is direct-list-initialization [...]. [...] Otherwise, obtainP
fromT
by replacing the occurrences ofauto
with either a new invented type template parameterU
or, if the initialization is copy-list-initialization, withstd::initializer_list<U>
. Deduce a value forU
using the rules of template argument deduction from a function call (14.8.2.1), whereP
is a function template parameter type and the corresponding argument is the initializer [...]. If the deduction fails, the declaration is ill-formed. Otherwise, the type deduced for the variable or return type is obtained by substituting the deducedU
intoP
.
因此,在const auto l2 = { 1, 2, 3 };
中,推导如同函数模板
template<class U> void meow(const std::initializer_list<U>);
给出调用 meow({1, 2, 3})
。
现在考虑无 const 的情况 auto l3 = { 1, 2, 3 };
(GCC 正确推断为 std::initializer_list<int>
)。这种情况下的推导就像函数模板
template<class U> void purr(std::initializer_list<U>);
给出调用 purr({1, 2, 3})
。
由于函数参数的顶级 cv 限定被忽略,很明显这两个推导应该产生相同的类型。
[temp.deduct.call]/p1:
Template argument deduction is done by comparing each function template parameter type (call it
P
) with the type of the corresponding argument of the call (call itA
) as described below. IfP
is a dependent type, removing references and cv-qualifiers fromP
givesstd::initializer_list<P'>
[...] for someP'
[...] and the argument is a non-empty initializer list (8.5.4), then deduction is performed instead for each element of the initializer list, takingP'
as a function template parameter type and the initializer element as its argument.
根据 1
、2
或 3
推导 P'
(即 U
),类型为 int
的所有文字,显然会产生 int
.