【问题标题】:C++ Templates: Automatically overload templated function with const& for constant params?C++ 模板:使用 const& 自动重载模板化函数以获得常量参数?
【发布时间】:2013-08-03 02:16:52
【问题描述】:

假设我有一个模板函数 foo(),它接受两个整数引用作为参数。我希望模板函数也能自动处理常量引用(例如来自常量的引用)。这是一个通用的例子。我可以让 foo() 工作,但我必须为引用/常量引用参数的每个排列提供一个新的实现。

#include <iostream>

using namespace std;

template<typename A, typename B>
void foo(A& a, B& b)
{
    cout<<"a:"<<a<<" b:"<<b<<endl;
}

template<typename A, typename B>
void foo(A& a, const B& b)
{
    cout<<"a:"<<a<<" b:"<<b<<endl;
}

template<typename A, typename B>
void foo(const A& a, B& b)
{
    cout<<"a:"<<a<<" b:"<<b<<endl;
}

template<typename A, typename B>
void foo(const A& a, const B& b)
{
    cout<<"a:"<<a<<" b:"<<b<<endl;
}

int main()
{
    int x = 0;

    foo(x, x);
    foo(x, 10);
    foo(10, x);
    foo(20, 20);

    return 0;
}

上面的例子有点做作,但它是对我正在尝试做的事情的概括。在我更复杂的情况下,我有一个类作为一组参数的包装器。类构造函数是模板化的,如 foo(),可以有多达 10 个参数。枚举所有 2^10 个可能的构造函数将是一场噩梦。

【问题讨论】:

  • 我承认自己不是专家,但这个问题听起来与完美转发有着惊人的关联(排列在那个方向上是一个不那么微妙的暗示)。 this question 的主要答案中有一个非常扎实的讨论,您可能值得一看。

标签: c++ templates metaprogramming template-meta-programming


【解决方案1】:

你描述的问题是完美转发问题。 C++11 用universal references 解决了这个问题:

template<typename A, typename B>
void foo(A&& a, B&& b) {
    bar(std::forward<A>(a), std::forward<B>(b));
}

这里的参数不是右值引用,而是通用引用。它们将具有与参数相同的 ref-ness 和 const-ness。

如果参数是右值,foo 中的参数将是带有名称的右值。命名的右值是左值。要将参数传递给具有保留值的子函数,您需要将它们包装在std::forward 中。函数bar 将得到与foo 完全相同类型的ab

【讨论】:

    【解决方案2】:

    如果模板不打算修改参数,那么只需提供带有const&amp; 的版本就可以了:

    template<typename A, typename B>
    void foo(const A& a, const B& b)
    {
        cout<<"a:"<<a<<" b:"<<b<<endl;
    }
    

    如果你传递一个非 const 左值,它仍然会被一个 const 引用绑定,一切都会正常工作。

    如果您想要一些重载来修改参数,那么请重新考虑设计,因为它们看起来不像应该共享名称的函数。有一些例外,例如,访问结构内部成员的访问器,如果对象是 const,您可能希望返回 const&amp;,否则返回非常量引用......如果是这种情况,您可以反其道而行之,只提供非常量重载:

    template<typename A, typename B>
    void foo(A& a, B& b)
    

    在这种情况下,如果参数是临时引用或非常量引用,则推断类型将反映它并将参数与const&amp; 绑定。

    int main() {
       int a = 5;
       const int b = 10;
       foo(a,b);          // foo<int,const int>(int&,const int&)
       foo(10,b);         // foo<const int,const int>(const int&, const int&)
    }
    

    重读您的问题,您似乎对完美转发感兴趣(这可能符合您的要求,也可能不符合您的要求)。如果是这种情况,并且如果您有 C++11 编译器,则可以将 universal-references 与可变参数模板一起使用。构建一个好的包装器是一件困难的事情,尽管您可以只使用std::tuple 作为实际存储,这应该会使任务变得非常简单。

    【讨论】:

    • 我决定将所有内容都设为 const...,然后将其丢弃。代码编译。这不是最安全的方法。一般情况下它永远不会起作用。但是,在这种特定情况下,它是安全的,因为还有其他安全措施可以防止临时堆栈过早超出范围。我很欣赏 C++11 的建议。完美的转发听起来可以解决问题。我们希望在一两年内开始在我们的项目中支持它(等待第 3 方更新)。
    【解决方案3】:

    除了所有正确的答案之外,这里有一个简单的程序仅供您参考,因为这可能有助于您更好地理解这个概念。

    #include<iostream>
    template<class X>
    void func(X& x, X& y)
    {
        //your code
    }
    template<class X, class Y>
    void intermediateFunc(X&& x, Y&& y)
    {
        func(x,y);
    }
    
    int main()
    {
        int y = 9;
        intermediateFunc(5,5);
        intermediateFunc(y,5);
        intermediateFunc(5,y);
        intermediateFunc(y,y);
    }
    

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2016-05-23
      • 2010-09-18
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多