C++ “我可以在编译时检测到吗?”;函数参数";它们是编译时常量

C++ “我可以在编译时检测到吗?”;函数参数";它们是编译时常量,c++,c++11,optimization,constexpr,C++,C++11,Optimization,Constexpr,我是否可以在编译时检测“函数参数”1是否为编译时常量 例如,一个函数print(int i),如果被称为print(5),它可以打印“常量5”,但是如果被称为print(i),它可以打印i是一些非常量变量。特别是,在“is constant”分支中,我应该能够将I视为constepr,包括将其用于模板参数等 宏技巧、模板元编程和SFINAE技巧都可以。理想情况下,它是可移植的,但是特定于编译器的解决方案总比没有好 如果存在“错误否定”,也就是说,如果常量值有时被检测为非常量(例如,当某些优化被禁

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

例如,一个函数
print(int i)
,如果被称为
print(5)
,它可以打印
“常量5”
,但是如果被称为
print(i)
,它可以打印
i
是一些非常量变量。特别是,在“is constant”分支中,我应该能够将
I
视为constepr,包括将其用于模板参数等

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

如果存在“错误否定”,也就是说,如果常量值有时被检测为非常量(例如,当某些优化被禁用时),这是可以的

如果解决方案能够检测何时将常量值间接传递给函数(例如,何时将常量值传递给调用
print
的中间函数,该函数随后内联,将常量公开给
print
),则会获得额外的积分。最后一种行为显然依赖于优化

如果它自然地扩展到多个参数,则会获得双倍的加分

如果可以有带和不带
constexpr
参数的重载函数版本,这可能很简单,但是



1我在这里引用“函数参数”,因为解决方案并不严格要求在函数中检测此状态(或在具有特殊参数的调用方/被调用方边界处)——它只需在调用方看来像函数,但宏或其他技巧,如带有
操作符()的静态对象< /P> >可使用。

< P>检测<代码> CONTXPRP</代码>适用性时,可考虑此<强> GCC仅< <强>(参见限制性链接答案):

它不一定是一个普通的函数void print(int i)-它可以是一个类似函数的宏,对其参数执行一些魔术,并根据它是常数还是模板魔术调用不同的函数

你说的“像宏一样的功能”

嗯。。。首先,我必须警告你,像宏这样的C风格函数是危险的。邪恶,伊姆霍

这么说来,如果您真的接受基于宏的解决方案,我想将它与
constepr
方法、模板
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; }
 };
请注意,在常量版本的
PrintStruct::func()
中,打印的值是一个模板整数值;so还可用于模板参数、C样式数组维度、
static\u assert()
s测试等

我不确定这是否是一个完美的标准(我不是一个真正的专家),并且它可以满足您的需求,但是下面是一个完整的工作示例

#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
 }
#包括
模板
结构打印结构
{
模板
静态void func(…)

{std::我记得GCC有类似于
\uuuu内置常量\uu p
的东西。然而,对于宏,我确信有更多的可移植方式,但该宏肯定必须面向用户。constexpr可以按照以下方式检测:也许这会有帮助?顺便说一句,你说“简单”用constexpr参数重载,但事实并非如此。当建议标准化时,这引发了巨大的讨论。@max66-对,但它不一定是一个普通函数
void print(int i)
-它可能是一个类似于宏的函数,对其参数执行一些魔法,并根据它是否是常数调用不同的函数,也可能是一个模板魔法。我的意思是代码的用户应该编写类似于打印(5)的东西
在内部,我希望能够采用
constexpr
代码路径,这取决于参数是否可以用作constexpr。您可以使用参数计数宏技巧的变体将宏解决方案扩展到多个参数(请参阅:),因此您可以定义
Print1
Print2
,…等,然后
Print(…)
将根据参数计数宏的结果调用右侧的
PrintN
宏。将宏的语句块用
do
while(0)包围
以便宏将扩展为语句。这避免了
else
的语法问题,例如
if(cond)Print(x);else{some;other;stuff;}这是C语言中的宏。对于C++,我想你可以尝试将你的块转换成一个直接调用的lambda,这会给你一个表达式。@ jxh -我已经和C(很多年前)工作了很多年。但我不记得这个把戏了;关于lambda……嗯,我看不出有什么办法可以做到这一点;但也许我错了……今天我没有时间,但也许明天,我会试试。如果你成功地将我的宏转换为lambda,请写下它作为答案。我对其他类型的问题感兴趣。@BeeOnRope-正确:不适用于表达式;about
val##i
是为了避免使用与本地静态变量相同的名称调用pass变量到
Print
;但是考虑到这会阻止使用表达式,我认为这是一个坏主意,最好为本地变量修复一个长而奇怪的名称。@BeeOnRope-macro修改为接受expressions(但不要使用包含
printLocalVar
:()的表达式调用它,除非我遗漏了什么,否则这需要调用方显式标记每个参数是否为常量。如果您愿意施加该限制,似乎有许多可能的解决方案-例如,只需调用
print()
而不是
打印(5)
用于常量参数(至少用于类型
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)
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); \
 }                                        \
()
#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
 }