我有点困惑。我有一个带有模板参数的模板类graph
-类vertex
,可以是对称或不对称的,压缩的或原始的,我只在运行时知道哪个。
因此,如果我想从磁盘上获取适当类型的图,在其上运行Bellman Ford然后释放内存,则需要在所有四个条件分支中重复模板实例化,如下所示:
#include "graph.h"
int main(){
// parse cmd-line args, to get `compressed` `symmetric`
// TODO get rid of conditionals.
if (compressed) {
if (symmetric) {
graph<compressedSymmetricVertex> G =
readCompressedGraph<compressedSymmetricVertex>(iFile, symmetric,mmap);
bellman_ford(G,P);
} else {
graph<compressedAsymmetricVertex> G =
readCompressedGraph<compressedAsymmetricVertex>(iFile,symmetric,mmap);
bellman_ford(G,P);
if(G.transposed) G.transpose();
G.del();
}
} else {
if (symmetric) {
graph<symmetricVertex> G =
readGraph<symmetricVertex>(iFile,compressed,symmetric,binary,mmap);
bellman_ford(G,P);
G.del();
} else {
graph<asymmetricVertex> G =
readGraph<asymmetricVertex>(iFile,compressed,symmetric,binary,mmap);
bellman_ford(G,P);
if(G.transposed) G.transpose();
G.del();
}
}
return 0;
}
问题:如何在条件限制之外提取除对readGraph
函数的调用之外的所有内容,并具有以下限制。
std::variant
,因为graph<T>
无法默认构造。 compressedAsymmetricVertex
设为vertex
的子类型,那么我无所不能。 编辑:这是示例标头graph.h
:
#pragma once
template <typename T>
struct graph{ T Data; graph(int a): Data(a) {} };
template <typename T>
graph<T> readGraph<T>(char*, bool, bool, bool) {}
template <typename T>
graph<T> readCompressedGraph<T> (char*, bool, bool) {}
class compressedAsymmetricVertex {};
class compressedSymmetricVertex {};
class symmetricVertex{};
class asymmetricVertex {};
答案 0 :(得分:2)
由于您没有列出所有类型,也没有说明binary
参数的作用,因此我只能给出一个近似的解决方案。根据您的实际需求对其进行优化。这应该符合:
class GraphWorker
{
public:
GraphWorker(bool compressed, bool symmetric)
: m_compressed(compressed), m_symmetric(symmetric)
{}
virtual void work(const PType & P, const char * iFile, bool binary, bool mmap ) const = 0;
protected:
const bool m_compressed;
const bool m_symmetric;
};
template <class GraphType>
class ConcreteGraphWorker : public GraphWorker
{
public:
ConcreteGraphWorker(bool compressed, bool symmetric)
: GraphWorker(compressed, symmetric)
{}
void work(const PType & P, const char * iFile, bool binary, bool mmap) const override
{
graph<GraphType> G =
readGraph<GraphType>(iFile, m_compressed, m_symmetric,
binary, mmap);
bellman_ford(G,P);
G.del();
}
};
static const std::unique_ptr<GraphWorker> workers[2][2] = {
{
std::make_unique<ConcreteGraphWorker<asymmetricVertex>>(false, false),
std::make_unique<ConcreteGraphWorker<symmetricVertex>>(false, true),
},
{
std::make_unique<ConcreteGraphWorker<compressedAsymmetricVertex>>(true, false),
std::make_unique<ConcreteGraphWorker<compressedSymmetricVertex>>(true, true),
}
};
int main()
{
workers[compressed][symmetric]->work(P, iFile, binary, mmap);
}
一些评论:最好完全避免bool
,并使用特定的枚举类型。这意味着您应该使用类似以下内容的东西代替我的二维数组:
std::map<std::pair<Compression, Symmetry>, std::unique_ptr<GraphWorker>> workers;
但是由于可能存在其他未知的依赖关系,我决定坚持使用令人困惑的bool
变量。同样,将workers
作为静态变量也有其缺点,并且由于我不知道您的其他要求,所以我也不知道该怎么做。另一个问题是基类中受保护的布尔变量。通常,我会改用访问器。
我不确定是否为了避免出现一些有条件限制而跳过所有这些花销,是否值得?这比原始代码更长,更棘手,并且除非有4个以上的选项,否则work()
中的代码会更长,所以我建议您坚持使用条件语句。
编辑:我刚刚意识到使用lambda函数可以说更清晰(这有待争论)。在这里:
int main()
{
using workerType = std::function<void(PType & P, const char *, bool, bool)>;
auto makeWorker = [](bool compressed, bool symmetric, auto *nullGrpah)
{
auto worker = [=](PType & P, const char *iFile, bool binary, bool mmap)
{
// decltype(*nullGraph) is a reference, std::decay_t fixes that.
using GraphType = std::decay_t<decltype(*nullGrpah)>;
auto G = readGraph<GraphType>(iFile, compressed, symmetric,
binary, mmap);
bellman_ford(G,P);
G.del();
};
return workerType(worker);
};
workerType workers[2][2] {
{
makeWorker(false, false, (asymmetricVertex*)nullptr),
makeWorker(false, true, (symmetricVertex*)nullptr)
},
{
makeWorker(true, false, (compressedAsymmetricVertex*)nullptr),
makeWorker(true, true, (compressedSymmetricVertex*)nullptr)
}
};
workers[compressed][symmetric](P, iFile, binary, mmap);
}
答案 1 :(得分:1)
简单的基准是,只要您想从“仅在运行时知道的类型”过渡到“在编译时必须知道的类型”(即模板),就需要一系列这样的条件。如果您根本无法修改graph
,那么无论何时您想在 non-em>中处理G
对象,您都将需要四个不同的G
变量(和分支)。模板化函数,因为所有graph
模板变体都是不相关的类型,不能统一对待(std::variant
除外)。
一种解决方案是,在读完compressed
和symmetric
之后,只进行一次正确的转换,并从那里保持完全模板化:
template<class VertexT>
graph<VertexT> readTypedGraph()
{
if constexpr (isCompressed<VertexT>::value)
return readCompressedGraph<VertexT>(/*...*/);
else
return readGraph<VertexT>(/*...*/);
}
template<class VertexT>
void main_T()
{
// From now on you are fully compile-time type-informed.
graph<VertexT> G = readTypedGraph<VertexT>();
bellman_ford(G);
transposeGraphIfTransposed(G);
G.del();
}
// non-template main
int main()
{
// Read parameters.
bool compressed = true;
bool symmetric = false;
// Switch to fully-templated code.
if (compressed)
if (symmetric)
main_T<compressedSymmetricVertex>();
else
main_T<compressedAsymmetricVertex>();
// else
// etc.
return 0;
}
您可能必须编写许多元函数(例如isCompressed
),但可以正常编写其他代码(尽管您的IDE不能提供太多帮助)。您丝毫没有受到任何限制。