[英]Is it bad to use #include in the middle of code?
我继续读到这样做是不好的,但我不认为这些答案完全回答了我的具体问题。
在某些情况下,它似乎真的很有用。 我想做类似以下的事情:
class Example {
private:
int val;
public:
void my_function() {
#if defined (__AVX2__)
#include <function_internal_code_using_avx2.h>
#else
#include <function_internal_code_without_avx2.h>
#endif
}
};
如果在这个例子中使用#include
代码中间是不好的,那么实现我想要做的事情的好方法是什么? 也就是说,我试图在avx2可用和不可编译的情况下区分成员函数实现。
不,它本质上不是坏事。 #include
旨在允许包含在任何地方。 只是这样使用它并不常见,这违背了最不惊讶的原则 。
围绕包含开发的良好实践都基于在编译单元开始时包含并且原则上在任何名称空间之外的假设。
这就是为什么C ++核心指南建议不要这样做,理解它们有正常的可重用标头:
SF.4:在文件中的其他声明之前包含.h文件
原因
最小化上下文依赖性并提高可读性。
补充说明:如何解决您的潜在问题
不确定完整的上下文。 但首先,我不会将函数体放在类定义中。 这将更好地封装类消费者的实现特定细节,这不应该知道。
然后你可以在正文中使用条件编译,或者更好地选择一些基于策略的设计 ,使用模板来配置在编译时使用的类。
我同意@Christophe所说的话。 在你的情况下,我会写下面的代码
写一个标题commonInterface.h
#pragma once
#if defined (__AVX2__)
void commonInterface (...) {
#include <function_internal_code_using_avx2.h>
}
#else
void commonInterface (...) {
#include <function_internal_code_without_avx2.h>
}
#endif
所以你隐藏了标题中#if defined
的#if defined
,并且在实现文件中仍然具有良好的可读代码。
#include <commonInterface>
class Example {
private:
int val;
public:
void my_function() {
commonInterface(...);
}
};
#ifdef __AVX2__
# include <my_function_avx2.h>
#else
# include <my_function.h>
#endif
class Example {
int val;
public:
void my_function() {
# ifdef __AVX2__
my_function_avx2(this);
# else
my_function(this);
# endif
}
};
它的好坏实际上取决于具体情况。 如果您必须编写大量的样板代码,通常会使用该技术。 例如,clang编译器在整个地方使用它来匹配/使用所有可能的类型,标识符,标记等。 下面是一个例子, 在这里一个又一个。
如果要根据某些编译时已知参数来定义不同的函数,那么将定义放在它们所属的位置会更清晰。 你不应该将foo
的定义分成两个单独的文件,并在编译时选择正确的文件,因为它会增加程序员(通常不仅仅是你)理解代码的开销。 考虑下面的代码片段,至少在我看来,它更具表现力:
// platform.hpp
constexpr static bool use_avx2 = #if defined (__AVX2__)
true;
#else
false;
#endif
// example.hpp
class Example {
private:
int val;
public:
void my_function() {
if constexpr(use_avx2) {
// code of "functional_internal_code_using_avx2.h"
}
else {
// code of "functional_internal_code_without_avx2.h"
}
};
通过更多概括,添加“仅定义算法”的抽象层而不是算法和平台特定的怪异,可以进一步改进代码。
反对你的解决方案的另一个重要论点是, functional_internal_code_using_avx2.h
和functional_internal_code_without_avx2.h
都需要特别注意:它们不会在没有example.h
情况下构建,如果不打开它们需要的任何文件就不会显而易见。 因此,必须添加构建项目时的特定标志/处理,一旦使用多个这样的functional_internal_code
-files就很难维护。
在你的情况下,我不确定你的大局是什么,所以无论如何,应该采取一些盐。
无论如何: #include
COULD发生在代码的任何地方,但你可以把它看作是一种分离代码/避免冗余的方法。 对于定义,其他方法已经很好地涵盖了这一点。 对于声明,这是标准方法。
现在,这个#include
被放在开头,作为对读者的礼貌,他们可以更快地赶上代码中应该遵循的内容,即使对于#ifdef
保护代码也是如此。
在您的情况下,您似乎想要相同功能的不同实现。 在这种情况下,to-go方法将链接代码的不同部分(包含不同的实现),而不是导入不同的声明。
相反,如果你想根据你的#ifdef
真的有一个不同的签名,那么我不会看到比在代码中间有#ifdef
更有效的方法。 但是,我不认为这是一个很好的设计选择!
我把它定义为对我不好的编码。 它使代码难以阅读。
我的方法是创建一个基类作为抽象接口,并创建专门的实现,然后创建所需的类。
例如:
class base_functions_t
{
public:
virtual void function1() = 0;
}
class base_functions_avx2_t : public base_functions_t
{
public:
virtual void function1()
{
// code here
}
}
class base_functions_sse2_t : public base_functions_t
{
public:
virtual void function1()
{
// code here
}
}
然后你可以有一个指向base_functions_t
的指针并实例化不同的版本。 例如:
base_functions_t *func;
if (avx2)
{
func = new base_functions_avx2_t();
}
else
{
func = new base_functions_sse2_t();
}
func->function1();
作为一般规则,我会说最好在实现文件中首先放置定义接口的头。
当然还有没有定义任何接口的标头。 我主要考虑使用宏hackery的标题,并且打算包含一次或多次。 这种类型的标头通常没有包含警戒。 一个例子是<cassert>
。 这允许您编写类似这样的代码
#define NDEBUG 1
#include <cassert>
void foo() {
// do some stuff
assert(some_condition);
}
#undef NDEBUG
#include <cassert>
void bar() {
assert(another_condition);
}
如果您只在文件的开头包含<cassert>
,则除了全部或全部关闭之外,您的实现文件中的断言没有粒度。 有关此技术的更多讨论,请参见此处 。
如果按照您的示例继续使用条件包含的路径,那么我强烈建议您使用Eclipse或Netbeans之类的编辑器来执行内联预处理器扩展和可视化。 否则,包含带来的局部性的丧失会严重损害可读性。
声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.