从C 模板中的非const l值参考中推导const l值参考

Deducing a const l-value reference from a non-const l-value reference in C++ template

本文关键字:值参 参考 const      更新时间:2023-10-16

假设您有以下功能:

void f(const int&) { 
    // Do something, making a copy of the argument.
}
void f(int&&) { 
    // Do the same thing, but moving the argument.
}

它们是相当多的多余的 - 功能是复制还是移动论点之间的唯一区别。当然,我们可以通过将其重写为单个模板函数来做得更好:

template<typename T> 
void g(T&&) { 
    // Do something, possibly using std::forward to copy or move the argument.
}

这有效,是实践中常用的成语。但是模板可以将模板实例化为三个函数,与上面的两个函数。我们可以通过以下代码来验证这一点:

#include <iostream>
template<typename T> constexpr char *type = nullptr;
template<> constexpr const char *type<int&> = "int&";
template<> constexpr const char *type<const int&> = "const int&";
template<> constexpr const char *type<int> = "int";
template<typename T> 
void g(T&&) { 
    std::cout << reinterpret_cast<void*>(&g<T>)
              << " = &g<" << type<T> << ">" << std::endl;
}
int main() {
    int i = 0;
    const int& cr = 0;
    g(i);
    g(cr);
    g(0);
    return 0;
}
/*
Prints:
0x100f45080 = &g<int&>
0x100f45100 = &g<const int&>
0x100f45180 = &g<int>
*/

这已经为T = int&的情况添加了第三个功能,当我们使用上面的非预测函数f时,这是我们没有的。在这种情况下,我们实际上并不需要该函数的非计算L值参考版本(构建的f足以满足我们的原始需求),而这会增加代码的大小,尤其是当我们编写了许多模板函数时,互相打电话的方式。

有没有办法在上面编写我们的函数g,以便编译器在我们的示例代码中调用g(i)时自动推论T = const int&?即,我们不必手动编写g<const int&>(i)但仍然得到所需的行为的方式。

这是一个主观的观点,可以说"正向引用"("通用引用")比专用的过载更好。当然,在很多情况下,这是真的,但是如果您想完全控制,他们将不会完成所有工作。

您可以通过添加

来明确确保用户不会通过非const lvalue参考
    static_assert(!std::is_lvalue_reference<T>::value || std::is_const<typename std::remove_reference<T>::type>::value, "only call g with const argument");

内部G,但这并不是在所有情况下是一个很好的解决方案。

否则您要为vector :: push_back(...)做的事情并提供显式过载 - 但这是您的起点,请参见https://en.cppreference.com/w/cpp/container/vector/推回。

"正确"的答案仅取决于您的要求。

编辑:@sjoerd的建议看起来像:

template <typename T>
class aBitComplicated {
public:
 void func(T&& v) { internal_func(std::forward<T>(v)); }
 void func(const T& v) { internal_func(v); }
private:
 template <typename U>
 void internal_func(U&& v) { /* your universal code*/ }
};

也有一些更复杂/复杂的版本,但这应该是最简单的版本,可以实现您所要求的。