我可以在编译时检测到编译时常量的"函数参数"

Bee*_*ope 12 c++ optimization constexpr c++11

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

例如,一个函数print(int i)可以"constant 5"在被调用时打印,print(5)"non-constant 5"如果被调用为print(i)where,i则是一些非常量变量.特别是,在"is constant"分支中,我应该能够将其i视为constexpr,包括将其用于模板参数等.

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

如果存在"错误否定"则可以 - 即,如果常量值有时被检测为非常数(例如,禁用某些优化时).

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

如果它自然延伸到多个参数,则可获得双倍奖励

如果一个人可以使用和不带constexpr参数重载函数的版本,这可能是直截了当的,但你不能.


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

Jul*_*ius 2

为了检测constexpr适用性,人们可以考虑来自 @JohannesSchaub-litb 的GCC-only 建议(有关限制,请参阅链接的答案):

template<typename T> 
constexpr typename remove_reference<T>::type makeprval(T && t) {
  return t;
}
Run Code Online (Sandbox Code Playgroud)

不同情况下的工作示例如下

#include <iostream>

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

// /sf/answers/931355071/

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));
  }
}
Run Code Online (Sandbox Code Playgroud)

使用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)
Run Code Online (Sandbox Code Playgroud)