gpt4 book ai didi

c++ - 使用std::initializer_list参数的非成员函数(/非构造函数上下文)的重载解析

转载 作者:行者123 更新时间:2023-12-01 14:42:43 28 4
gpt4 key购买 nike

(以下所有ISO标准引用均引用N4659: March 2017 post-Kona working draft/C++17 DIS,并且所有示例程序结果在GCC和Clang上对于C++ 11,C++ 14和C++ 17都是一致的)

考虑以下示例:

#include <initializer_list>

// Denote as A.
void f(float) {}

// Denote as B.
void f(std::initializer_list<int>) {}

int main() {
// Denote call as C1.
f(1.5F); // Overload resolution picks A (trivial).

// Denote call as C2.
f({1.5F}); // Overload resolution picks B (and subsequently fails; narrowing).

return 0;
}

这导致调用C2将重载B选为最佳可行函数,随后由于列表初始化范围变窄而失败(由 [dcl.init.list]/7控制):

error: type 'float' cannot be narrowed to 'int' in initializer list [-Wc++11-narrowing]



  • 为什么f({1.5F});函数调用发现void f(std::initializer_list<int>)是唯一的最佳可行函数,其排名比void f(float)更好? Afaics这与[over.ics.list]/4[over.ics.list]/9冲突(请参阅下面的详细信息)。

  • 我正在寻找有关相关标准段落的引用。

    请注意,我知道有关列表初始化(构造函数重载)和 std::initializer_list<>(以及有关此主题的各种SO问题)的重载解析的特殊规则,其中, std::initializer_list<>是首选,由 [over.match.list]/1来控制。但是,狂热,这在这里不适用(或者,如果我错了,至少可以说与 [over.ics.list]/4中显示的标准示例有冲突)。

    我自己的细节和调查

    对于上述C1和C2调用,都按照 [over.call.func](根据 [over.match]/2.1[over.match.call]/1)(特别是 [over.call.func]/3)中的指定应用了重载解析,并且这两个调用的候选函数集为:
    candidate_functions(C1) = { void f(float),
    void f(std::initializer_list<int>) }

    candidate_functions(C2) = { void f(float),
    void f(std::initializer_list<int>) }

    每个调用中的参数列表与调用中的表达式列表相同。

    根据 [over.match.viable]/2[over.match.viable]/3,可行函数集与两个调用的候选函数相同:
    viable_functions(C1) = { void f(float),
    void f(std::initializer_list<int>) }

    viable_functions(C2) = { void f(float),
    void f(std::initializer_list<int>) }

    最佳可行功能

    简而言之,按照 [over.match.best]/1的规定,给定函数调用的最佳可行函数(此处特别是单参数调用(每个调用的可行函数没有特殊情况))是对于单个参数而言,从相应的可行函数的单个参数到单个参数,具有最佳的隐式转换顺序。

    C1:最佳可行功能

    调用 f(1.5F)具有身份标准转换(不转换),因此可以(平分和明确地)选择A的精确匹配项(按照 [over.ics.scs]/3)作为最佳可行函数。
    best_viable_function(C1) = void f(float)

    C2:最佳可行功能

    Afaics,按照 [over.ics.list],将 f({1.5F})的隐式转换序列对可行候选者进行排名由 [over.ics.list]/1控制:

    When an argument is an initializer list ([dcl.init.list]), it is not an expression and special rules apply for converting it to a parameter type.



    将C2匹配到

    对于单个参数为基本 float类型的A的匹配,可应用 [over.ics.list]/9,尤其是 [over.ics.list]/9.1 [ 强调矿]:

    Otherwise, if the parameter type is not a class:

    • (9.1) if the initializer list has one element that is not itself an initializer list, the implicit conversion sequence is the one required to convert the element to the parameter type; [ Example:

      void f(int);
      f( {'a'} ); // OK: same conversion as char to int
      f( {1.0} ); // error: narrowing

      — end example ]

    • [...]



    可以说这意味着将调用 f({1.5F}}匹配到 f(float)的隐式转换序列与 floatfloat的转换序列相同;即身份转换以及随后的完全匹配。但是,按照上面的示例,由于调用C2甚至不会导致模棱两可的最佳可行函数,因此我的逻辑中肯定存在一些缺陷。

    将C2与B匹配

    为了匹配使用单个参数 [over.ics.list]/4的B [ 强调地雷]:

    Otherwise, if the parameter type is std​::​initializer_­list<X> and all the elements of the initializer list can be implicitly converted to X, the implicit conversion sequence is the worst conversion necessary to convert an element of the list to X, or if the initializer list has no elements, the identity conversion. This conversion can be a user-defined conversion even in the context of a call to an initializer-list constructor. [ Example:

    void f(std::initializer_list<int>);
    f( {} ); // OK: f(initializer_­list<int>) identity conversion
    f( {1,2,3} ); // OK: f(initializer_­list<int>) identity conversion
    f( {'a','b'} ); // OK: f(initializer_­list<int>) integral promotion
    f( {1.0} ); // error: narrowing

    [...]

    — end example ]



    因此,在此示例中,隐式转换序列是将列表中的单个 float元素转换为 int所需的最差转换,这是一个转换排序的标准转换序列( [over.ics.scs]/3),尤其是按照 [conv.fpint]/1变窄的转换。

    最佳可行功能

    根据我对以上标准段落的解释,最佳可行功能应与C1调用相同,
    best_viable_function(C2) = void f(float) ?

    但我显然缺少了一些东西。

    最佳答案

    列表初始化序列:一种序列转换为std::initializer_list时的特殊情况排序

    [over.ics.rank]/3.1适用于这种情况,并且优先于[over.ics.rank]/3的其他规则[强调矿]:

    List-initialization sequence L1 is a better conversion sequence than list-initialization sequence L2 if

    • (3.1.1) L1 converts to std​::​initializer_­list<X> for some X and L2 does not, or, if not that
    • (3.1.2) [...]

    even if one of the other rules in this paragraph would otherwise apply. [ Example:

    void f1(int);                                 // #1
    void f1(std::initializer_list<long>); // #2
    void g1() { f1({42}); } // chooses #2

    void f2(std::pair<const char*, const char*>); // #3
    void f2(std::initializer_list<std::string>); // #4
    void g2() { f2({"foo","bar"}); } // chooses #4

     — end example ]



    表示分别适用于通过标准转换序列和用户定义的转换序列来区分隐式转换序列的 [over.ics.rank]/3.2[over.ics.rank]/3.3不适用,这又意味着 [over.ics.list]/4[over.ics.list]/9在比较最佳转换时将不用于对隐式转换序列进行排名匹配“C2 call A”与“C2 call B”。

    这个话题是C++ 11和C++ 14中的缺陷

    这些转换可能会违反直觉,并且控制它们的规则很复杂也就不足为奇了。在原始的C++ 11和C++ 14 ISO标准版本中, f({1.5F});调用实际上具有含糊的排名规则。最佳可行的功能,已包含在 CWG Defect Report 1589 [ 强调我的]中:

    1589. Ambiguous ranking of list-initialization sequences

    Section: 16.3.3.2 [over.ics.rank]
    Status: CD4
    Submitter: Johannes Schaub
    Date: 2012-11-21

    [Moved to DR at the November, 2014 meeting.]

    The interpretation of the following example is unclear in the current wording:

    void f(long);
    void f(initializer_list<int>);
    int main() { f({1L});

    The problem is that a list-initialization sequence can also be a standard conversion sequence, depending on the types of the elements and the type of the parameter, so more than one bullet in the list in 16.3.3.2 [over.ics.rank] paragraph 3 applies:

    [...]



    对于上面的示例,这些项目符号给出相反的结果,
    是在其中选择的实现差异。

    [...]

    拟议的解决方案(2014年6月):

    此问题已通过解决问题得到解决
    1467

    CWG Defect Report 1467最终还解决了DR 1589,特别是在[ 强调矿]的上方添加了[over.ics.rank] / 3中引用的相关部分:

    1467. List-initialization of aggregate from same-type object

    [...]

    Proposed resolution (June, 2014):

    [...]

    1. Move the final bullet of 16.3.3.2 [over.ics.rank] paragraph 3 to the beginning of the list and change it as follows:

    [...]

    even if one of the other rules in this paragraph would otherwise apply. [Example: ... — end example]

    This resolution also resolves issues 1490, 1589, 1631, 1756, and 1758.



    此后,诸如GCC和Clang之类的编译器已将DR 1467向后移植到较早的标准(C++ 11和C++ 14)。

    关于c++ - 使用std::initializer_list参数的非成员函数(/非构造函数上下文)的重载解析,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/62302073/

    28 4 0
    Copyright 2021 - 2024 cfsdn All Rights Reserved 蜀ICP备2022000587号
    广告合作:1813099741@qq.com 6ren.com