【发布时间】:2013-05-09 15:11:27
【问题描述】:
在我正在编写的库中,我有一个类:
template<typename T>
class my_class {};
还有一个功能:
template<typename T>
void my_function(...) {};
如果我的代码的用户尝试实例化my_class<T>,我想保证他们在他们的代码中的某处调用了my_function<T>。换句话说,如果他们尝试实例化my_class 模板而不实例化相应的my_function 模板,我想生成一个编译错误。
例子:
int main() {
my_func<int>() // cause instantiation of my_func<int>
my_class<int> foo; // okay, because my_func<int> exists
my_class<char> bar; // compile error! my_func<char> does not exist
}
请注意,my_class<T> 不需要在内部使用 my_func<T>,因此实例化它不会自动导致 my_func<T> 被实例化。
编辑[0]:我自己不能调用my_func<T>,因为我需要用户调用它并传递告诉库如何处理my_class<T> 的信息。 IE。如果他们想使用my_class<char>,他们需要在代码的特定位置明确说明。我可以让my_class<T> 构造函数测试my_func<T> 是否已被调用,如果没有,则生成运行时错误,但我希望能够生成编译时错误,因为在编译时可以知道@ 987654336@ 曾经被使用过。
编辑[1]:一种不太理想的方法是创建一个 MY_FUNC 宏,该宏创建一个模板特化,否则不完整的模板类...
template<typename T>
class incomplete;
#define MY_FUNC(T) template<> incomplete<T> { static const bool x = my_func<T>(); };
template<typename T>
class my_class {
template<size_t i>
class empty {};
typedef empty<sizeof(incomplete<T>)> break_everything;
}
现在,如果在某处使用了MY_FUNC(T),则用户只能使用my_class<T>。但是,我更喜欢不使用宏且不强制用户在全局范围而不是在函数中使用 MY_FUNC 的解决方案。
编辑[2]:感谢大家的回答。我意识到在编译时不可能保证用户不会错误地调用my_func<T>。但我可以让它不太可能发生,并让它在早期产生运行时错误。
他们应该定义一个名为initialise_library 的函数。函数如下所示:
void initialise_library() {
my_func<int>();
my_func<char>();
etc..
}
在库初始化期间,它会检查以确保没有调用任何 my_funcs。然后它调用initialise_library。然后它会检查以确保所有my_funcs 都已被调用。这是可行的。如果用户没有定义initialise_library,他们会得到一个链接器错误。如果他们没有在他们的程序中的某个地方调用my_func<T> 来获取他们使用的my_class<T>,那么(理想情况下)他们会得到一个编译错误。如果他们定义了initialise_library并且他们在某处使用了my_func<T>但是他们在错误的地方使用了它,当他们的程序启动时会出现运行时错误起来。
基本上,我想阻止他们在代码中的某处添加my_class<T> 并忘记将my_func<T>() 放入initialise_library,因为这是他们可能会做的事情。运行时检查的唯一方法是在my_class<T> 的构造过程中。如果他们只在深入且很少使用的代码块中使用my_class<T>,那么进行此检查将是一个令人讨厌的延迟时间。
【问题讨论】:
-
erm,如果您需要用户传递东西,那么您似乎在编译时没有足够的信息,因此检查构造函数似乎是唯一可行的选择
-
“如果我的代码的用户尝试实例化 my_class
”,我想保证我的代码的用户在他们的代码中的某处调用了 my_function - 要么A) 糟糕的设计,或 B) 您需要记录的内容。尝试重新考虑整个决定。” -
int x = 0; cin >> x; if(x == 1) { my_class<whatever> m; } my_function<whatever>();你能告诉我是否在调用你的函数之前创建了一个实例吗? -
查看编辑[2]了解我如何进行此项检查。
-
我在尝试将过程 C 库包装在 C++ 类(例如 OpenGL)中时遇到了类似的情况。因此,我理解为什么需要这个,但如果这真的是一个库,你永远不能指望调用者将所有初始化放在 1 个方法中并在此之后实例化类。您始终可以将此记录为使用该库的良好实践,但最终如何使用它取决于用户。所以尽量减少这些限制,这使得使用库非常困难。解决这个问题的一种方法可能是工厂模式。