[英]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.