当使用“完美转发”时,来电者如何知道要传递的类型?

时间:2015-12-23 04:35:44

标签: c++ c++11

我最近听说过“完美转发”(来自Scott Meyers Effective modern C ++ ),

案例1.不使用Perfect Forward

class Widget {
public:
    void setName(const std::string& newName) { name = newName; }
    void setName(std::string&& newName) { name = std::move(newName); }
...

案例2.使用Perfect Forward

class Widget {
public:
    template<typename T>
    void setName(T&& newName) { name = std::forward<T>(newName); }
...

所以现在,我知道使用完美转发有很多好处。但在案例1中,用户知道setName的参数类型是std::string。但是,在案例2中,它不能。

所以我的问题是:

  1. 使用完美转发时,用户如何知道要传递的参数类型?
  2. 如何克服这个缺点?

3 个答案:

答案 0 :(得分:3)

您(代码的作者)必须告诉用户可接受哪些类型。对于示例中显示的代码,请注释

  

T类型的对象应分配给std::string类型的左值。

可能会有所帮助。否则,他们将从他们的编译器错误消息中学习它,这确实可能非常令人沮丧。

你可以使用SFINAE来限制可接受的T的设置,但是我不知道你会从中获得什么,除非如果你的限制太多,它可能会阻止一些理想的优化(喜欢忘记允许const char *)。它所购买的只是交换一个编译器错误(“不能将T分配给std::string”)另一个(“不能过载以调用Widget::setName(T)”)。当该功能还有其他超载候选者时,图片会发生变化,但请再次参阅Scott Meyer关于(而不是)重载完美转发器的书。

与任何强大功能一样,不要过度使用完美转发功能。如果您觉得收益不会超过您的用例中的额外复杂性,请随意避免使用它。一个简单的“按值计算并应用std::move”通常是一个很好的和充分的选择。

答案 1 :(得分:0)

我认为这里的解决方案是按名称传递名称:

class Widget {
public:
    void setName(std::string newName) { name = std::move(newName); }
...

所以这里如果名称是rvalue,那么它将被移动。 如果它是左值,它将被复制,因为你在const引用重载中复制它,没有变化。

答案 2 :(得分:-2)

模板的重点是使代码与底层类型无关。完美的转发是有效的,因为它转发&#34;正确的类型。 (l值保持l值,r值保持r值)

您在问题中显示的代码不会编译,因为std :: forward需要调用一个类型。如果您只是想尝试std :: forward,那么您的限制代码将类似于:

template<typename T> 
class Widget {
public:
    void setName(T name) { static_assert(sizeof(T) == -1, "class must be instantiated with std::string"); }
};
template<>
class Widget<string>{
public:
    void setName(string pname) { name = std::forward<string>(pname); }
    string name;
};

请注意,这不是完美转发的意思。有关详情,请查看this: Why does a perfect forwarding function have to be templated?

相关问题