由于某种原因,我认为允许C ++ 0x std::initializer_list 作为期望可以从此构建类型的函数的函数参数,例如 std::vector. 。但是显然,它行不通。这只是我的编译器,还是它永远不会起作用?是因为潜在的超载解决问题吗?

#include <string>
#include <vector>

void function(std::vector<std::string> vec)
{
}

int main()
{
    // ok
    std::vector<std::string> vec {"hello", "world", "test"};

    // error: could not convert '{"hello", "world", "test"}' to 'std::vector...'
    function( {"hello", "world", "test"} );
}
有帮助吗?

解决方案

GCC有一个错误。该标准使此有效。看:

请注意,这有两个方面

  • 一般如何进行初始化?
  • 在超负荷分辨率期间如何使用初始化,它的成本是多少?

第一个问题在部分中回答 8.5. 。第二个问题在部分中回答 13.3. 。例如,参考绑定在 8.5.313.3.3.1.4, ,而列表初始化已在 8.5.413.3.3.1.5.

8.5/14,16:

以形式发生的初始化

T x = a;

以及参数通过, ,函数返回,抛出异常(15.1),处理异常(15.3)和汇总成员初始化(8.5.1)称为拷贝性初始化。
.
.
初始化器的语义如下[...]:如果初始化器是支撑式列表,则该对象是列表initialized(8.5.4)。

在考虑候选人时 function, ,编译器将看到一个初始化列表(尚无类型 - 它只是语法构造!)作为参数,并且 std::vector<std::string> 作为参数 function. 。弄清楚转换成本是什么以及我们是否 能够 在超载的背景下转换这些, 13.3.3.1/5

13.3.3.1.5/1:

当参数是初始化列表(8.5.4)时,它不是表达式,并且特殊规则适用于将其转换为参数类型。

13.3.3.1.5/3:

否则,如果该参数是每13.3.1.7的非聚合X类,而过载分辨率是从参数初始化器列表中执行X类型X对象的初始化的单个最佳构造函数,则隐式转换序列是用户 - 定义的转换序列。允许用户定义的转换将初始化器列表元素转换为构造函数参数类型,但如13.3.3.1所述。

非聚合类 Xstd::vector<std::string>, ,我将在下面找出最佳的构造函数。最后一个规则使我们在以下情况下使用用户定义的转换:

struct A { A(std::string); A(A const&); };
void f(A);
int main() { f({"hello"}); }

我们被允许将字符串文字转换为 std::string, ,即使这需要用户定义的转换。但是,它指出了另一个段落的限制。有什么 13.3.3.1 说?

13.3.3.1/4, ,这是负责禁止多个用户定义的转换的段落。我们只会查看列表初始化:

但是,在考虑用户删除的转换函数[(或构造函数)]的参数时,该函数是[...] 13.3.1.7的候选者,当将初始化程序列表传递为单个参数或初始化器列表完全具有一个元素时对于X或[...]的构造函数的第一个参数,考虑到某个类X类或引用(可能是CV-Qualifif)的X,仅允许标准转换序列和省略号转换序列。

请注意,这是一个重要的限制:如果不是为此,以上可以使用复制构建器来建立同样良好的转换序列,并且初始化将是模棱两可的。 (请注意该规则中“ A或B和C”的潜在混乱:它是说“(A或B)和C” - 因此我们受到限制 只要 试图通过具有类型参数的X构造函数转换 X).

我们被委派 13.3.1.7 对于收集构造函数,我们可以用来进行此转换。让我们从一般一侧开始本段 8.5 将我们委派给 8.5.4:

8.5.4/1:

列表初始化可能发生在直接启动或拷贝定性上下文中;在直接启动上下文中的列表限制称为 直接列表初始化 在复制定位化上下文中的列表初始化称为 复制名单初始化.

8.5.4/2:

构造函数是 初始化列表构造函数 如果其第一个参数是类型 std::initializer_list<E> 或参考可能是CV的标志 std::initializer_list<E> 对于某种类型的E,并且没有其他参数,否则所有其他参数都有默认参数(8.3.6)。

8.5.4/3:

定义对象的列表限制或T型的引用如下:[...]否则,如果T是类型,则考虑构造函数。如果t具有初始列表构造函数,则参数列表由单个参数组成。否则,参数列表由初始化器列表的元素组成。列举了适用的构造函数(13.3.1.7),最好的构造函数是通过超载分辨率(13.3)选择的。

此时, T 是类类型 std::vector<std::string>. 。我们有一个参数(尚无类型!我们只是在具有语法初始化器列表的上下文中)。构建器被列举 13.3.1.7:

...]如果t具有初始化列表构造函数(8.5.4),则该参数列表由单个参数组成;否则,参数列表由初始化器列表的元素组成。对于拷贝列表初始化,候选函数是T的所有构造函数。但是,如果选择了显式构造函数,则初始化是错误的。

我们只考虑 std::vector 作为唯一的候选人,由于我们已经知道其他人不会赢得胜利,也不适合该论点。它具有以下签名:

vector(initializer_list<std::string>, const Allocator& = Allocator());

现在,将初始化程序列表转换为一个的规则 std::initializer_list<T> (对参数/参数转换的成本进行分类) 13.3.3.1.5:

当参数是初始化列表(8.5.4)时,它不是表达式,并且特殊规则适用于将其转换为参数类型。 [...]如果参数类型为 std::initializer_list<X> 并且初始化列表的所有元素都可以隐式转换为x,隐式转换序列是将列表的元素转换为x的最糟糕的转换。 这种转换可以是用户限制的转换 即使是在调用初始列表构造函数的情况下。

现在,初始化列表将成功转换,转换序列是用户定义的转换(从 char const[N]std::string)。这是如何详细介绍的 8.5.4 再次:

否则,如果T是 std::initializer_list<E>, ,如下所述构造一个initializer_list对象,并根据对象的初始化对象初始化对象的初始化(8.5)。 (...)

8.5.4/4 如何迈出最后一步:)

其他提示

它似乎是这样起作用的:

function( {std::string("hello"), std::string("world"), std::string("test")} );

也许这是一个编译器错误,但也许您要求太多隐性转换。

我不确定,但我怀疑这里发生了什么是转换为initializer_list是一种转换,将其转换为向量是另一种转换。如果是这样,您将仅超过一个隐式转换的限制...

这要么是编译器错误,要么您的编译器不支持std :: prinitizer_list。在GCC 4.5.1上进行了测试,并且可以很好地编译。

您必须指定initializer_list的类型

function(std::initializer_list<std::string>{"hello", "world", "test"} );

祝你好运

许可以下: CC-BY-SA归因
不隶属于 StackOverflow
scroll top