C++ 如何防止带有模板的整个API仅成为.h文件的集合?
注意:如果您想将此标记为重复,请花点时间阅读整个问题。我自己也提到了下面的其他相关问题,但是这些问题都是在C++语言层。这个问题也在C++语言层,但这个问题也涉及API的API开发,涉及到其他问题似乎没有解决的模板。 我向我的用户公开了一个API(客户端代码)。我的API很大程度上依赖于模板 什么不起作用 以下是我的API代码:C++ 如何防止带有模板的整个API仅成为.h文件的集合?,c++,templates,declaration,api-design,definition,C++,Templates,Declaration,Api Design,Definition,注意:如果您想将此标记为重复,请花点时间阅读整个问题。我自己也提到了下面的其他相关问题,但是这些问题都是在C++语言层。这个问题也在C++语言层,但这个问题也涉及API的API开发,涉及到其他问题似乎没有解决的模板。 我向我的用户公开了一个API(客户端代码)。我的API很大程度上依赖于模板 什么不起作用 以下是我的API代码: // api.h #include <vector> template<typename T> void api_func(std::vect
// api.h
#include <vector>
template<typename T> void api_func(std::vector<T> v);
现在可以编译了
$ clang++ -std=c++11 client.cpp && ./a.out
5
问题
该解决方案表明,如果我不想在API中提交模板参数的特定类型(以确保客户端能够灵活地选择适合其需要的类型),那么依赖模板参数的任何函数都需要在头文件本身中定义
但当我对API中的所有函数执行此操作时,似乎所有业务逻辑都已移到头文件中。所以我的整个API现在是一个巨大的.H文件集合,绝对没有.CPP文件
我的问题是:
- 我是否做错了什么,导致我的整个API实现只包含.H文件而不包含.CPP文件
- 这些API通常是如何设计的?有没有其他技术可以将实现代码带回.CPP文件中,这样就只有尽可能少的内容进入.H文件
- 您已经从问题中删除了决定如何解决问题的部分。模板的主体关系到如何准确地隐藏它们(或者是否可能)。但我会用你给我的回答
你的问题基本上是类型擦除的问题
从这个开始:
template<typename T> void api_func(std::vector<T> v);
这里,我们从向量中提取.size()
,然后对其进行处理
然后我们将其流式传输到cout
的事实并不是关于传入的类型的事实;我们希望它有一个.size()
的事实是
在cpp文件中:
void api_func(has_dot_size_ref v) {
// This prints just the size, but in the actual API, we would be
// doing more complex things.
std::cout << v.size() << '\n';
}
void api\u func(有点大小参考v){
//这只打印大小,但在实际的API中,我们将
//做更复杂的事情。
coutboost主要是一个只包含头的库,我不认为这有什么错(还有很多其他只包含头的库的例子)我不确定是对有趣的问题投赞成票,还是对遗漏解决该问题的具体实例所需的细节投反对票。最后我投了反对票。@cheers-sandhth.-Alf您希望我在我的问题中包括什么样的细节?如果您的意思是,我应该包括我正在开发的确切API的细节,不是吗这不适合用于堆栈溢出?确切的API详细信息是数百行代码,因此我认为我应该在这里提供一个最小的玩具示例来演示我的问题。你认为呢?这对此类问题没有真正的帮助,因为深入到细节会涉及一些来回的交流。但是您可以创建一个简单的示例,至少演示API的一些数据流和控制流。如果没有这个示例,您得到的只是一般性建议“类型擦除”。“您已经从问题中删除了决定如何解决它的部分。”-我应该在我的问题中包含什么样的附加信息?我这样问是因为我看到了对我的问题的类似评论,所以我很想知道我在问题中遗漏了什么。“从问题类型中找出你到底需要什么”-假设我在API实现中调用了std::vector
的所有成员函数。这会改变您的答案吗?@您试图通过删除函数的功能来简化。但函数的功能决定了如何解决此问题。Ivtype erased callong size;您可以在元素o上键入erase CALIMG其他方法如果一个容器只做一个前缀,然后调用每个元素,它就不算是一个库,并且在头中擦除之后,就没有剩下什么了。在其他情况下,你可以擦除一个小的细节,并且对于实现来说还有很多的复杂性。
// api.h - fixed
#include <iostream>
#include <vector>
template<typename T> void api_func(std::vector<T> v)
{
// This prints just the size, but in the actual API, we would be
// doing more complex things.
std::cout << v.size() << '\n';
}
# api.cpp is unnecessary now
rm api.cpp
# client.cpp remains the same
$ clang++ -std=c++11 client.cpp && ./a.out
5
template<typename T> void api_func(std::vector<T> v);
template<typename T> void api_func(std::vector<T> v) {
// This prints just the size, but in the actual API, we would be
// doing more complex things.
std::cout << v.size() << '\n';
}
struct has_dot_size_ref {
template<class T,
std::enable_if_t< !std::is_same<T, has_dot_size_ref>{}, bool> = true
>
has_dot_size_ref( T const& t ):
ptr( std::addressof(t) ),
call_dot_size([](void const* ptr)->std::size_t{
return static_cast<T const*>(ptr)->size();
})
{}
std::size_t size() const {
return call_dot_size(ptr);
}
private:
void const* ptr = 0;
std::size_t(*call_dot_size)(void const*) = 0;
};
void api_func(has_dot_size_ref v);
void api_func(has_dot_size_ref v) {
// This prints just the size, but in the actual API, we would be
// doing more complex things.
std::cout << v.size() << '\n';
}