[英]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
方法,模板struct
, static
局部变量和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.