3

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

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()成员函数更改为返回 a std::string,而不是 a const std::string&

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

在哪些情况下,此更改可能会触发运行时故障?

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

4

4 回答 4

4

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

此外,他们有可能调用不同的重载,并且特征会以不同的方式专业化,但我不会担心这一点。

于 2012-11-12T10:33:49.697 回答
1

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

从理论上讲,这可能允许用户滥用返回值?也许?

顺便提一句。我会这样解决这个问题:

static std::string empty_string;
const std::string& get_name() const { return empty_string; }
于 2012-11-12T10:29:47.927 回答
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)您犯了一个错误,这不是基于模板的编译方式-time 多态性应该起作用,并且 (b)non_mutex没有相同的成员函数签名和返回类型。

于 2012-11-12T11:09:13.480 回答
0

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

如果你想学究气,你总是可以根据 SFINAE 做出选择,因为你已经模板化了代码。有了它,您可以完全删除non_mutex::get_name().

于 2012-11-12T11:09:27.570 回答