【问题标题】:In std::exchange, why is the second template parameter defaulted?在std::exchange中,为什么第二个模板参数默认了?
【发布时间】:2016-04-24 22:29:10
【问题描述】:

C++14 标准为std::exchange 指定以下声明:

template <class T, class U = T>
T std::exchange(T& obj, U&& new_value);

我想知道为什么U 默认为T,因为U 可以通过new_value 找到。在什么情况下,这会导致不同的结果:

template <class T, class U>
T std::exchange(T& obj, U&& new_value);

【问题讨论】:

    标签: c++ templates stl c++14 default-arguments


    【解决方案1】:

    std::exchangeN3511 中提出 默认模板参数,后来N3608 带有默认模板参数。请注意,在 N3608 中提供了以下推理:

    给第二个模板参数一个默认值可以修复 以下两种情况:

    DefaultConstructible x = ...;
    if (exchange(x, {})) { ... }
    
    int (*fp)(int);
    int f(int);
    double f(double);
    /*...*/ exchange(fp, &f) /*...*/
    

    第一个例子的用处当然是一个无类型的临时{} 将被推导出为T。第二个例子涉及更多:

    14.8.2 模板参数推导[temp.deduct]

    5 得到的替换和调整函数类型用作 模板实参推导的函数模板类型。 如果一个 模板参数没有推导出来及其对应的模板 参数有一个默认参数,模板参数是确定的 通过替换为前面确定的模板参数 模板参数转换为默认参数。 如果替换 导致类型无效,如上所述,类型推导失败。

    14.8.2.5 从类型推导模板参数 [temp.deduct.type]

    4 在大多数情况下,类型、模板和非类型值 用于组合 P 参与模板参数推导。那是, 它们可用于确定模板参数的值,并且 如此确定的值必须与确定的值一致 别处。然而,在某些情况下,该值不 参与类型推导,而是使用模板的值 在别处推导出来或明确指定的论点。 如果模板参数仅在非推导上下文中使用并且是 未明确指定,模板参数推导失败。

    5 未推断的上下文是:

    (5.5) — 不能进行实参推导的函数形参 完成,因为关联的函数参数是一个函数,或 一个集合 重载函数(13.4),以及以下一项或多项 申请:

    (5.5.1) — 多个函数匹配函数参数 类型(导致模棱两可的推论)

    在第二个示例中,模板参数U 仅在非推导上下文中使用,因为f(int)f(double) 两个重载都可以匹配到U。因此,不会进行参数推导,U 成为提供的默认值T(在这种情况下为int (*)(int),因此选择了f(int))。

    此外,正如@VladfromMoscow 所解释的,在传递std::exchange&lt;T&gt;(例如标准算法)时,具有默认参数允许更短的代码。

    【讨论】:

    • 啊!我的心都炸了。这甚至合法吗?看起来f 被定义了两次。
    • @JonathanMee 声明了f 的两个重载。
    • @JonathanMee,基本函数重载!?
    • 所以,fp 现在是 int (*)(int),实际上引用了 double (*)(double)?这至少会发出警告吗?或者exchange 是否允许这样做?
    • @JonathanMee,不,它指向 int(int) 函数。 f....的第一次重载
    【解决方案2】:

    函数可以作为参数传递给其他函数或算法。在这种情况下,如果函数的两个参数具有相同的类型,则只指定第一个模板参数就足够了。

    这使代码更短,更易读。

    这是一个人为的例子。:)

    #include <iostream>
    #include <numeric>
    #include <iterator> 
    #include <functional>
    
    
    int main()
    {
        int a[] = { 1, 2, 3 };
        int b[] = { 4, 5, 6 };
    
        std::cout << "a: ";
        for ( int x : a ) std::cout << x << ' ';
        std::cout << std::endl;
        std::cout << "b: ";
        for ( int x : b ) std::cout << x << ' ';
        std::cout << std::endl;
    
        auto sum = std::inner_product( std::begin( a ), std::end( a ),
                                       std::make_move_iterator( std::begin( b ) ), 0,
                                       std::plus<int>(), std::exchange<int> );
    
        std::cout << "sum = " << sum << std::endl;
        std::cout << "a: ";
        for ( int x : a ) std::cout << x << ' ';
        std::cout << std::endl;
    }
    

    输出是

    a: 1 2 3 
    b: 4 5 6 
    sum = 6
    a: 4 5 6 
    

    或者示例可以包含转换

    #include <iostream>
    #include <numeric>
    #include <iterator> 
    #include <functional>
    
    
    int main()
    {
        int a[] = { 1, 2, 3 };
        double b[] = { 4.4, 5.5, 6.6 };
    
        std::cout << "a: ";
        for ( int x : a ) std::cout << x << ' ';
        std::cout << std::endl;
        std::cout << "b: ";
        for ( double x : b ) std::cout << x << ' ';
        std::cout << std::endl;
    
        auto sum = std::inner_product( std::begin( a ), std::end( a ),
                                       std::make_move_iterator( std::begin( b ) ), 0,
                                       std::plus<>(), std::exchange<int> );
    
        std::cout << "sum = " << sum << std::endl;
        std::cout << "a: ";
        for ( int x : a ) std::cout << x << ' ';
        std::cout << std::endl;
    }
    

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2012-08-07
      • 2021-09-03
      • 2013-09-29
      相关资源
      最近更新 更多