返回副本而不是const引用时,我会破坏代码吗?

时间:2012-11-12 10:26:14

标签: c++

我需要使用的框架定义了一个简单的互斥类,它可以存储互斥锁所有者的名称,以帮助调试:

class mutex
{
public:
    explicit mutex(const std::string& mutex_owner = "");

    bool acquire() const;
    bool release() const;

    const std::string& get_name() const {return owner_name_;}

    // ...

private:
    std::string owner_name_;

    // ...
};

我刚刚更改了一些算法,使互斥锁类型成为模板参数,因此如果不需要锁定,我可以为了性能原因传入这个算法:

class non_mutex
{
public:
    explicit non_mutex(const std::string& mutex_owner = "")     {}

    bool acquire() const               {return true;}
    bool release() const               {return true;}

    std::string get_name() const {return "";}
};

由于这个没有存储名称(不需要调试),我更改了get_name()成员函数以返回std::string,而不是const std::string&

现在我的问题是:这个 默默 会破坏什么吗?代码编译得很好,并且似乎运行良好,但是在这个代码库中几乎没有测试,并且这个函数主要仅在出现问题时使用,而不是经常使用。

此更改可能导致运行时故障的情况如何?

请注意,这是一个C ++ 03环境,但我也对C ++ 11的答案感兴趣。

4 个答案:

答案 0 :(得分:4)

按值返回的那个可能会抛出一个错误的alloc,引用一个是无抛出的。所以这可能是一个问题。

此外,他们有可能调用不同的重载,特征会有所不同,但我不担心这个。

答案 1 :(得分:1)

好吧,你不再回归常数了。你正在返回一个临时的。

理论上这可能会让用户误用返回值?也许?

顺便说一下。我会这样解决问题:

static std::string empty_string;
const std::string& get_name() const { return empty_string; }

答案 2 :(得分:1)

对于静默断点,一个区别是返回返回/引用的对象的生命周期。例如,请考虑以下代码:

const string &stupid_user(const string &s) { return s; }

const string &name = stupid_user(mtx.get_name());
mtx.acquire();
std::cout << name;

现在,如果mtx的类型为mutex,则会在acquire之后打印互斥锁当前所有者的名称。如果mtx具有类型non_mutex,则它具有未定义的行为(在这种情况下,const引用不会延长临时的生命周期)。未明确的行为显然允许可能通过您的测试。

用不太愚蠢的用户:

const string &name = mtx.get_name();
mtx.acquire();
std::cout << name;

现在mutex的行为是打印新的所有者,non_mutex打印旧的所有者。也许你的测试会捕获它,也许它们没有,但是如果调用代码假定为一个并且你提供的类型是另一个类型,那么你已经默默地破坏了调用代码。

或者怎么样:

auto &&name = mtx.get_name();
mtx.acquire();
std::cout << name;

我认为这与非愚蠢用户的行为相同,但我不确定。

如果您(或此问题的未来访问者)对嘈杂的破损感兴趣,那么这取决于您如何定义允许使用Mutex概念的表达式(您希望您提供的两个类都满足)。

例如,如果允许表达式&mtx.get_name(),那么non_mutex就不能满足概念的要求。

如果您不允许该表达式,那么non_mutex可能满足要求 - 请仔细查看允许调用get_name的表达式。如果你所要求的只是它的返回值是“可转换为string”或其他一些,那么你没事。

如果你没有根据允许的表达式定义模板参数的要求,而是根据它具有的成员函数签名和返回类型来定义,那么(a)你犯了一个错误,那不是模板的方式基于编译时的多态性应该起作用,(b)non_mutex没有相同的成员函数签名和返回类型。

答案 3 :(得分:0)

我觉得您的更改没有任何问题。 get_name()都返回不可修改的左值;尝试修改它们会导致C ++ 03中的编译器错误。

如果你想变得迂腐,你总是可以根据SFINAE做出选择,因为你已经模仿了代码。有了它,您可以完全删除non_mutex::get_name()