从C ++模板中的非const l值引用推导出const l值引用

Pau*_*ill 5 c++ templates perfect-forwarding c++11 template-argument-deduction

假设您具有以下一对功能:

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

void f(int&&) { 
    // Do the same thing, but moving the argument.
}
Run Code Online (Sandbox Code Playgroud)

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

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

这有效,并且在实践中是常用的惯用语。但是该模板可能被实例化为三个函数,而上面的两个函数又是如此。我们可以使用以下代码来验证这种情况:

#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>
*/
Run Code Online (Sandbox Code Playgroud)

这为when的情况添加了第三个函数T = int&,而在使用f上面的非模板函数时我们没有。在这种情况下,我们实际上不需要此函数的此非常量l值引用版本- f足以满足我们的原始需求-这会增加代码的大小,尤其是当我们以这种方式编写许多模板函数时互相呼唤。

是否可以在g上面的代码中编写函数,以便编译器自动推断出示例代码中的T = const int&何时g(i)调用?即,一种无需手动编写g<const int&>(i)但仍可获得所需行为的方法。

Ral*_*ich 3

说“前向引用”(“通用引用”)比专用重载更好是一种主观观点。当然,在很多情况下确实如此,但如果你想完全控制,他们不会做所有的工作。

您可以通过添加显式确保用户不会传递非常量左值引用

    static_assert(!std::is_lvalue_reference<T>::value || std::is_const<typename std::remove_reference<T>::type>::value, "only call g with const argument");
Run Code Online (Sandbox Code Playgroud)

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

或者您执行为 vector::push_back(...) 所做的操作并提供显式重载 - 但这是您的起点,请参阅https://en.cppreference.com/w/cpp/container/vector/push_back

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

编辑:@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*/ }
};

Run Code Online (Sandbox Code Playgroud)

还有一个更复杂/复杂的版本,但这应该是实现您要求的最简单的版本。