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

时间:2019-03-06 23:32:55

标签: c++ c++11 templates perfect-forwarding template-deduction

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

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,以便在示例代码中调用T = const int&时,编译器会自动推断出g(i)?也就是说,我们不必手动编写g<const int&>(i)仍能获得所需的行为。

1 个答案:

答案 0 :(得分: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");
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*/ }
};

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