13

那是我的问题。我只是好奇关于限制可以传递给通用函数或类的类型的共识是什么。我想我在某个时候读过,如果你正在做通用编程,通常最好让事情保持开放而不是试图关闭它们(不记得源代码)。

我正在编写一个具有一些内部通用函数的库,我觉得它们应该只允许库中的类型与它们一起使用,因为这就是我要使用它们的意思。另一方面,我不确定我为锁定事物所做的努力是否值得。

有人可能对这个话题有一些统计资料或权威评论吗?我也对合理的意见感兴趣。希望这不会使这个问题完全无效:\

此外,这里是否有任何标签等同于“最佳实践”?我没有具体看到那个,但是能够为给定的 SO 主题提供所有最佳实践信息似乎会有所帮助......也许不是,只是一个想法。

编辑:到目前为止,一个答案提到我正在做的图书馆类型很重要。它是一个数据库库,最终与 STL 容器、可变参数(元组)、Boost Fusion 以及类似的东西一起工作。我可以看到这有什么关系,但我也对确定走哪条路的经验法则感兴趣。

4

5 回答 5

14

始终尽可能打开它- 但请确保

  • 记录有效类型所需的接口和行为以与您的泛型代码一起使用。
  • 使用类型的接口特性(特征)来确定是否允许/禁止它。不要根据类型名称做出决定。
  • 如果有人使用错误的类型,则产生合理的诊断。如果 C++ 模板被错误的类型实例化,那么它们非常擅长引发大量嵌套的错误——使用类型特征、静态断言和相关技术,可以轻松生成更简洁的错误消息。
于 2011-05-18T16:26:14.587 回答
3

这是 STL 最强大的卖点之一,它是如此开放,它的算法适用于我的数据结构以及它自己提供的数据结构,我的算法适用于它的数据结构以及我的数据结构。

让您的算法对所有类型开放还是将它们限制为您自己的算法是否有意义,很大程度上取决于您正在编写的库,我们对此一无所知。

(最初我的意思是说,泛型编程的意义在于广泛开放,但现在我看到泛型总是有限制的,你必须在某处划清界限。它也可能仅限于你的类型,如果这有意义的话。)

于 2011-05-18T16:27:28.467 回答
3

在我的数据库框架中,我决定放弃模板并使用单个基类。通用编程意味着可以使用任何所有对象。特定类型类超过了少数通用操作。例如,可以比较字符串和数字是否相等;BLOB(二进制大对象)可能想要使用不同的方法(例如比较存储在不同记录中的 MD5 校验和)。

此外,字符串和数字类型之间存在继承分支。

通过使用继承层次结构,我可以使用Field类或专用类(例如Field_Int.

于 2011-05-18T17:20:52.213 回答
2

至少在 IMO,正确的做法大致是概念所尝试的:与其尝试验证您是否收到指定类型(或一组指定类型之一),不如尽最大努力指定类型的要求,并验证您收到的类型是否具有正确的特征,并且可以满足您的模板要求。

就像概念一样,这样做的大部分动机是在不满足这些要求时简单地提供好的、有用的错误消息。最终,如果有人试图通过不满足其要求的类型实例化您的模板,编译器将产生错误消息。问题是,除非您采取措施确保它确实有用,否则该错误消息可能不会很有帮助。

于 2011-05-18T17:18:40.470 回答
2

问题

如果您的客户可以在公共标头中看到您的内部函数,并且如果这些内部通用函数的名称是“通用的”,那么您可能会让您的客户面临意外调用您的内部通用函数的风险。

例如:

namespace Database
{

// internal API, not documented
template <class DatabaseItem>
void
store(DatabaseItem);
{
    // ...
}

struct SomeDataBaseType {};

}  // Database

namespace ClientCode
{

template <class T, class U>
struct base
{
};

// external API, documented
template <class T, class U>
void
store(base<T, U>)
{
    // ...
}

template <class T, class U>
struct derived
    : public base<T, U>
{
};

}  // ClientCode

int main()
{
    ClientCode::derived<int, Database::SomeDataBaseType> d;
    store(d);  // intended ClientCode::store
}

在这个例子中,作者main甚至不知道 Database::store 的存在。他打算调用 ClientCode::store,然后变得懒惰,让 ADL 选择函数而不是指定ClientCode::store. 毕竟,他的论点store来自同一个命名空间,store所以它应该可以正常工作。

它不起作用。此示例调用Database::store. 根据Database::store此调用的内部情况,可能会导致编译时错误,或者更糟糕的是,会导致运行时错误。

怎么修

您对函数的命名越笼统,发生这种情况的可能性就越大。给你的内部函数(必须出现在你的标题中的那些)真正的非通用名称。或者将它们放在像details. 在后一种情况下,您必须确保您的客户端永远不会将其details作为用于 ADL 的关联命名空间。这通常通过不创建客户端将直接或间接使用的类型来实现namespace details

如果您想变得更加偏执,请开始使用enable_if.

如果您认为您的内部功能可能对您的客户有用,那么它们不再是内部的。

上面的示例代码并不牵强。它发生在我身上。它发生在namespace std. store在这个例子中,我称它过于通用std::advance并且std::distance是过度通用代码的经典示例。这是需要防范的。这是一个试图解决的问题概念。

于 2011-05-18T17:59:40.100 回答