我可以在编译时检测到编译时常量

时间:2017-12-06 00:54:04

标签: c++ c++11 optimization constexpr

我可以在编译时检测“函数参数” 1 是否是编译时常量?

例如,如果调用print(int i),可以打印"constant 5"print(5)如果调用"non-constant 5" print(i),其中ii非常数变量。特别是,在“is constant”分支中,我应该能够将print视为constexpr,包括将其用于模板参数等。

宏技巧,模板元编程和SFINAE技巧都可以。理想情况下它是可移植的,但是编译器特定的解决方案总比没有好。

如果存在“假阴性”则可以 - 即,如果有时将常数值检测为非常数(例如,禁用某些优化时)。

如果解决方案可以检测到常量值间接传递给函数的时间点(例如,当一个常量值传递给调用print的中间函数并且随后内联将常量暴露给{{ 1}})。最后一种行为显然取决于优化。

如果它自然延伸到多个论点,则可获得双倍奖励。

如果可以使用constexpr个参数重载函数的重载版本,这可能很简单,但you can't

1 我在这里引用“函数参数”,因为解决方案并不严格要求在函数内(或在具有特殊参数的调用者/被调用者边界)检测此状态 - 它只需要像调用函数一样向调用者显示,但可以使用宏或其他技巧,如operator()等静态对象。

2 个答案:

答案 0 :(得分:3)

  

它不一定是普通函数void print(int i) - 它可能是一个类似函数的宏,它的参数做了一些魔术,并根据它是一个常量调用一个不同的函数,或者它可能是一些模板魔术

“像功能一样的宏”你说过吗?

嗯......首先,我必须警告你C风格的功能宏是危险的。蒸馏邪恶,恕我直言。

说这个,如果你真的接受一个基于宏的解决方案,我想将它与struct方法,模板staticPrintStruct局部变量和SFINAE ...

如果您定义以下模板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; } };

foo

以及以下C风格的函数式宏,用于定义struct本地PrintStruct并将其作为模板参数传递给func()以激活SFINAE以选择所需的{ {1}}(并且明显地调用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
 }

答案 1 :(得分:2)

为了检测constexpr适用性,我们可以考虑仅限GCC suggestion from @JohannesSchaub-litb(请参阅相关的限制答案):

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)