簡體   English   中英

我可以在編譯時檢測到編譯時常量的“函數參數”

[英]Can I detect at compile time “function arguments” that are compile-time constants

我可以在編譯時檢測“函數參數” 1是否是編譯時常量?

例如,一個功能print(int i)可打印"constant 5"如果被稱為print(5)"non-constant 5"如果被稱為print(i)其中, i是一些非恆定變量。 特別是,在“is constant”分支中,我應該能夠將i視為constexpr,包括將其用於模板參數等。

宏技巧,模板元編程和SFINAE技巧都可以。 理想情況下它是可移植的,但是編譯器特定的解決方案總比沒有好。

如果存在“錯誤否定”則可以 - 即,如果常量值有時被檢測為非常數(例如,禁用某些優化時)。

如果解決方案可以檢測到何時將常量值間接傳遞給函數(例如,當常量值傳遞給調用print的中間函數並且隨后內聯時將常量暴露給print ),則可以獲得獎勵積分。 最后一種行為顯然取決於優化。

如果它自然延伸到多個參數,則可獲得雙倍獎勵

如果一個人可以在有和沒有constexpr參數的情況下重載函數的版本,這可能是直截了當的,但你不能


1我在這里引用“函數參數”,因為解決方案並不嚴格要求在函數內(或在具有特殊參數的調用者/被調用者邊界)檢測此狀態 - 它只需要像函數一樣出現給調用者但是可以使用宏或其他技巧,例如帶有operator()等的靜態對象。

它不必是一個普通的函數void print(int i) - 它可以是一個類似函數的宏,它在它的參數上做一些魔術,並根據它是一個常量調用一個不同的函數,或者它可能是一些模板魔術

“功能像宏”你說?

嗯......首先,我必須警告你,C風格的功能宏是危險的。 蒸餾邪惡,恕我直言。

說這個,如果你真的接受基於宏的解決方案,我constexpr它與constexpr方法,模板structstatic局部變量和SFINAE結合起來......

如果您定義以下模板PrintStruct struct

template <typename T>
struct PrintStruct
 {
   template <bool>
   static void func (...) 
    { std::cout << "func non-const: " << T::func(true) << std::endl; }

   template <bool b, int I = T::func(b)>
   static void func (int) 
    { std::cout << "func const:     " << I << std::endl; }
 };

和下面的C風格的函數式宏,定義一個foo本地struct並將其作為模板參數傳遞給PrintStruct以激活SFINAE以選擇所需的func() (並顯然調用func() )[ 編輯 :宏由jxh改進,使其作為聲明擴展; 謝謝!] [ 編輯2 :宏觀修改,觀察OP后,接受表達式]

#define Print(i)                          \
[&]()                                     \
 {                                        \
   static int const printLocalVar { i };  \
                                          \
   struct local_foo                       \
    {                                     \
      static constexpr int func (bool b)  \
       { return b ? printLocalVar : 0; }  \
    } ;                                   \
                                          \
   PrintStruct<local_foo>::func<true>(0); \
 }                                        \
()

觀察到PrintStruct::func()的const版本中的打印值是模板整數值; 所以也可以用於模板參數,C風格的數組維度, static_assert()的測試等。

不確定這是完全標准的(我不是一個真正的專家),並做你想要的,但以下是一個完整的工作示例

#include <iostream>

template <typename T>
struct PrintStruct
 {
   template <bool>
   static void func (...) 
    { std::cout << "func non-const: " << T::func(true) << std::endl; }

   template <bool b, int I = T::func(b)>
   static void func (int) 
    { std::cout << "func const:     " << I << std::endl; }
 };


#define Print(i)                          \
[&]()                                     \
 {                                        \
   static int const printLocalVar { i };  \
                                          \
   struct local_foo                       \
    {                                     \
      static constexpr int func (bool b)  \
       { return b ? printLocalVar : 0; }  \
    } ;                                   \
                                          \
   PrintStruct<local_foo>::func<true>(0); \
 }                                        \
()

int main()
 {
   constexpr int  i { 2 };
   int const      j { 3 };
   int            k { 4 };
   int const      l { k+1 };

   Print(1);    // print func const:     1
   Print(i);    // print func const:     2
   Print(j);    // print func const:     3
   Print(k);    // print func non-const: 4
   Print(l);    // print func non-const: 5
   Print(2+2);  // print func const:     4
 }

為了檢測constexpr適用性,我們可以考慮來自@ JohannesSchaub-litb的這個僅限GCC的 建議 (參見鏈接的限制答案):

template<typename T> 
constexpr typename remove_reference<T>::type makeprval(T && t) {
  return t;
}

一個包含不同案例的工作示例

#include <iostream>

////////////////////////////////////////////////////////////////////////////////

// https://stackoverflow.com/a/13305072/2615118

template<class T>
constexpr std::remove_reference_t<T> makeprval(T&& t) {
  return t;
}

#define isprvalconstexpr(e) noexcept(makeprval(e))

////////////////////////////////////////////////////////////////////////////////

template<bool is_constexpr, class Lambda>
struct HybridArg {
  using T = std::invoke_result_t<Lambda>;

  Lambda lambda_;
  constexpr operator T() const { return lambda_(); }// implicit conversion
};

template<bool is_constexpr, class Lambda>
constexpr auto make_hybrid_arg(Lambda lambda) {
  return HybridArg<is_constexpr, Lambda>{lambda};
}

#define WRAP_ARG(arg)                     \
  make_hybrid_arg<isprvalconstexpr(arg)>( \
    [&] { return arg; }                   \
  )                                       \

////////////////////////////////////////////////////////////////////////////////

template<int i>
void print_impl_constexpr() {
  std::cout << i << ": ";
  std::cout << __PRETTY_FUNCTION__ << std::endl;
}

void print_impl_fallback(int i) {
  std::cout << i << ": ";
  std::cout << __PRETTY_FUNCTION__ << std::endl;
}

////////////////////////////////////////////////////////////////////////////////

// option 1 (for choosing implementation):
// compile-time introspection

template<class Arg>
struct is_constexpr_arg : std::false_type {};

template<class Lambda>
struct is_constexpr_arg<
  HybridArg<true, Lambda>
> : std::true_type {};

template<class Arg>
void print_by_introspection(Arg arg) {
  if constexpr(is_constexpr_arg<Arg>{}) {
    print_impl_constexpr<arg>();
  }
  else {
    print_impl_fallback(arg);
  }
}

////////////////////////////////////////////////////////////////////////////////

// option 2 (for choosing implementation):
// overload

void print_by_overload(int arg) {
  print_impl_fallback(arg);
}

template<class Lambda>
void print_by_overload(HybridArg<true, Lambda> arg) {
  print_impl_constexpr<arg>();
}

////////////////////////////////////////////////////////////////////////////////

template<class Arg>
void indirection(Arg arg) {
  print_by_introspection(arg);
  print_by_overload(arg);
}

void bad_indirection(int arg) {
  print_by_introspection(arg);
  print_by_overload(arg);
}

////////////////////////////////////////////////////////////////////////////////

int main() {
  {
    int i = 0;
    indirection(i);
  }
  {
    int i = 1;
    indirection(WRAP_ARG(i));
  }
  {
    constexpr int i = 2;
    indirection(WRAP_ARG(i));
  }
  {
    constexpr int i = 3;
    bad_indirection(WRAP_ARG(i));
  }
}

使用GCC編譯后的輸出:

0: void print_impl_fallback(int)
0: void print_impl_fallback(int)
1: void print_impl_fallback(int)
1: void print_impl_fallback(int)
2: void print_impl_constexpr() [with int i = 2]
2: void print_impl_constexpr() [with int i = 2]
3: void print_impl_fallback(int)
3: void print_impl_fallback(int)

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM